#include "iostream"
using namespace std;
class A // 4+4 = 8
{
public:
int a;
virtual void p0() {}
virtual void p() { cout << "A" << endl; }
};
class B : public A
{
public:
void p() { cout << "B" << endl; }
};
int main()
{
A * b = new B;
b->p();
delete b;
return 0;
}
看一下汇编
A * b = new B;
008B5D48 push 8
008B5D4A call operator new (08B13B1h)
008B5D4F add esp,4
008B5D52 mov dword ptr [ebp-0D4h],eax
008B5D58 cmp dword ptr [ebp-0D4h],0
008B5D5F je main+54h (08B5D74h)
008B5D61 mov ecx,dword ptr [ebp-0D4h]
008B5D67 call B::B (08B14BAh)
008B5D6C mov dword ptr [ebp-0E8h],eax
008B5D72 jmp main+5Eh (08B5D7Eh)
008B5D74 mov dword ptr [ebp-0E8h],0
008B5D7E mov eax,dword ptr [ebp-0E8h]
008B5D84 mov dword ptr [b],eax // 将子类对象的内存入口赋给b指针
b->p();
008B5D87 mov eax,dword ptr [b]
008B5D8A mov edx,dword ptr [eax]
008B5D8C mov esi,esp
008B5D8E mov ecx,dword ptr [b]
008B5D91 mov eax,dword ptr [edx+4] // 取出虚函数表中的函数地址
008B5D94 call eax
008B5D96 cmp esi,esp
008B5D98 call __RTC_CheckEsp (08B12E9h)
思路很清晰,子类对象通过访问属于自己的虚函数表来调用子类方法
如果不用虚函数呢?
#include "iostream"
using namespace std;
class A // 4+4 = 8
{
public:
int a;
virtual void p0() {}
void p() { cout << "A" << endl; }
};
class B : public A
{
public:
void p() { cout << "B" << endl; }
};
int main()
{
A * b = new B;
b->p();
delete b;
return 0;
}
反汇编:
b->p();
00DF2207 mov ecx,dword ptr [b]
00DF220A call A::p (0DF14E2h)
可以看到,如果不是虚函数,对象调用自己的方法在编译期间就已经确定了,对于A *a = new B这种,普通的调用方法在编译期间是直接替换为A::p的