A problem is the memory footprint of the resulting executable. Not that I didn't find C++ appealing, but the overhead is just too big for our little machines. So chances are high that you're not even getting a somewhat slower, but also a "too large for your target" application.
I don't accept this as an argument, as this has been constantly used for last 2-3 decades, without any hard data. Whatsoever.
We have 4 MB expansions now. Even without that, we got 128 KB in stock Atari. And 256-320 KB expansions were pretty common over a decade ago. That's a lot for 6502 code.
Let me do a bit of a research work here, in this thread, so we are on the same page. Here's the quick draft of the base class common to all players and enemies in an hypothetical RPG game:
class CParameters
{
public:
char Strength;
char Speed;
char Agility;
char Armor;
char Health;
char Level;
char Experience;
void Damage (char value)
{
Health -= value;
}
void Heal (char value);
{
Health += value;
}
void Initialize ()
{
Experience = 0;
Level = 1;
Health = 100;
Armor = 0;
Strength = 1;
Speed = 1;
Agility = 1;
}
CParameters ()
{
Initialize ();
}
};
Here's the declarations and instantiations. Note that they're global, hence at compile-time we (compiler) already know all addresses.
// The following instances are global, thus removing any need for run-time instantiation and heap memory management
CParameters Player;
// Each enemy's parameters will be set upon encountering him in the dungeon
CParameters EnemyGhost;
CParameters EnemyZombie;
CParameters EnemySpider;
There's billion ways how to implement it, so the following one is just one of the many and I don't claim it's the fastest, shortest, but I do believe it's fast enough:
- each class has 7 Bytes of data
- compiler thus can determine the address of each global instance:
$1000:Player
$1007:EnemyGhost
$100E:EnemyZombie
$1015:EnemySpider
- the code for the methods will address the class variables via 0-page ptr : InstancePtr
Here's what a code for Initialize could look like:
CParameter_Initialize:
;Strength
LDA #1
LDY #0
STA (InstancePtr),Y
;Speed
LDA #1
LDY #1
STA (InstancePtr),Y
;Agility
LDA #1
LDY #2
STA (InstancePtr),Y
;Armor
LDA #0
LDY #3
STA (InstancePtr),Y
;Health
LDA #100
LDY #4
STA (InstancePtr),Y
;Level
LDA #1
LDY #5
STA (InstancePtr),Y
;Experience
LDA #0
LDY #6
STA (InstancePtr),Y
RTS
; When a method call is encountered, compiler will simply put the address of the instance into InstancePtr (10 cycles) and call the method:
LDX #$10 ; 2c
LDY #$00 ; 2c
STX InstancePtrHi ; 3c
STY InstancePtrLo ; 3c
JSR CParameter_Initialize ;
Now, here's the code for Heal/Damage (for the example's simplicity, does not deal with overflows)
CParameters_Heal:
LDY #4
LDA FunctionParameter1
CLC
ADC (InstancePtr),Y
STA (InstancePtr),Y
RTS
CParameters_Damage:
LDY #4
LDA (InstancePtr),Y
SEC
SBC FunctionParameter1
STA (InstancePtr),Y
RTS
; Upon encountering the calls to these methods, compiler - seeing that there's just 1 parameter - could avoid the stack, and just store the desired value to FunctionParameter1 address and then just JSR to the function:
LDA ValueOfParameter ; 4c
STA FunctionParameter1 ; 4c
JSR CParameters_Heal ; 6c
So, we finally get to the actual run-time usage:
This call
......
Player.Heal (MediumMedkitValue);
......
can be translated as:
LDX #$10 ; 2c
LDY #$00 ; 2c
STX InstancePtrHi ; 3c
STY InstancePtrLo ; 3c
LDA MediumMedkitValue ; 4c
STA FunctionParameter1 ; 4c
JSR CParameter_Heal ; 6c
Of course, compiler should also store/restore the previous value of InstancePtr, as those calls may be nested, but that's outside of scope of this example.
24 cycles. Damn! Of 29,895c only 29,871 cycles are remaining, at 50 fps (160x96x4) ! Too slow !!! Back to BASIC 
Sarcasm aside, the default usage scenario, without virtual methods can be fast (and even those are really only a double look-up, so far from the horrors that C++ books make it out to be). Of course, the above example is simplified, as compiler has to take into account few other things, but there's no reason, that it couldn't have a codepath like the above, when it encounters this particular case.