inline 声明为虚函数
class Base
{
public:
//这里将内联函数声明为虚函数
virtual inline void Show() { cout << "Base::show()" << endl; }
private:
int ma;
};
class Derived:public Base
{
public:
void Show() { cout << "Derived::Show()" << endl; }
};
int main()
{
Base* p = new Derived();
p->Show();
return 0;
}
这里分析一下p->Show()的反汇编代码
p->Show();
//将p的里面存的值放到eax寄存器, 这里是Derived对象的地址
00864B1D mov eax,dword ptr [p]
//将eax(Derived) 里面的值的前四个字节(dword 双字)的内容放到edx寄存器
//这里是vfptr的值,因为该对象的前四个字节是虚函数表的地址
00864B20 mov edx,dword ptr [eax]
//将esp 寄存器的内容放在 esi 寄存器
00864B22 mov esi,esp
//将p指向的地址存放在ecx中
00864B24 mov ecx,dword ptr [p]
//取出虚函数表的前四个字节,(第一个虚函数的地址)
00864B27 mov eax,dword ptr [edx]
//调用虚函数
00864B29 call eax
因为inline是将函数的代码在调用处展开,不存在函数栈帧的开辟,更没有函数地址,而虚函数表中存储的是每个虚函数的地址,所以这里的inline 是不起作用的。
static 修饰的静态成员方法能不能声明为虚函数
静态函数又叫类的成员方法,静态成员函数的调用是不依赖对象的存在的,也就是在创建对象之前,就可以调用静态成员函数。
而虚函数存在于虚函数表中,虚函数表在编译阶段生成,访问虚函数的时候,首先在对象的前四个字节中找到虚函数表的地址,然后在虚函数表中查找相应的虚函数的地址所以说虚函数的访问是依赖对象存在的而静态成员方法不能依赖对象的存在,所以二者是不能兼容的。
关于虚函数表于虚函数指针的内容参考
https://blog.csdn.net/qq_43390943/article/details/89420382
构造函数能不能是虚函数
跟前面static 修饰的类似,如果构造函数可以是虚函数,那调用构造函数之前对象并不存在,即无法通过对象的vfptr 指针找到虚函数表的地址,也就无法访问构造函数。
(与死锁的原理相似,只有调用构造函数才能产生对象,只有产生对象才能调用构造函数)
调用虚函数一定发生动态绑定吗?
通过上面的汇编代码的分析,我们可以看到,动态绑定时调用函数的汇编指令是
call eax 直接call 一个 寄存器,而静态绑定通常是call + 地址,这里通过这个特征看一下什么时候动态绑定,什么时候静态绑定
int main()
{
//通过对象进行调用
Derived d;
d.Show();
}
Derived d;
00C34A41 call Derived::Derived (0C31564h)
d.Show();
00C34A46 lea ecx,[d]
//通过对象调用是静态绑定
00C34A49 call Derived::Show (0C3155Fh)
int main()
{
//通过基类指针访问
Base* p = new Derived();
p->Show();
return 0;
}
p->Show();
00F84A7D mov eax,dword ptr [p]
00F84A80 mov edx,dword ptr [eax]
00F84A82 mov esi,esp
00F84A84 mov ecx,dword ptr [p]
00F84A87 mov eax,dword ptr [edx]
//动态绑定
00F84A89 call eax
这里不一一测试,通过基类指针或引用,派生类指针或引用访问都是动态绑定
通过对象访问都是静态绑定
构造函数中调用虚函数
前面说过虚函数的动态绑定依赖于对象的存在,构造函数结束之前对象还没有存在,所以在构造函数中调用虚函数,应该是静态绑定,这里给出验证
class Base
{
public:
Base() { Show(); }
virtual void Show() { cout << "Base::show()" << endl; }
private:
int ma;
};
int main()
{
Base p;
p.Show();
return 0;
}
//汇编指令
Base p;
00974ACE lea ecx,[p]
//这里进入构造函数
00974AD1 call Base::Base (097155Ah)
/***********************构造函数中************************/
0097497F pop ecx
00974980 mov dword ptr [this],ecx
00974983 mov eax,dword ptr [this]
00974986 mov dword ptr [eax],97DD60h
0097498C mov ecx,dword ptr [this]
//静态绑定
0097498F call Base::Show (0971555h)
析构函数中调用虚函数
这个要分两种情况
首先,析构函数是虚函数
class Base
{
public:
virtual void Show() { cout << "Base::show()" << endl; }
virtual ~Base() { Show(); }
private:
int ma;
};
int main()
{
Base *p = new Base();
delete p;
return 0;
}
00904B5F mov edx,dword ptr [ebp-0E0h]
00904B65 mov eax,dword ptr [edx]
00904B67 mov ecx,dword ptr [ebp-0E0h]
00904B6D mov edx,dword ptr [eax+4]
//动态绑定
00904B70 call edx
析构函数是普通函数
class Base
{
public:
virtual void Show() { cout << "Base::show()" << endl; }
~Base() { Show(); }
private:
int ma;
};
int main()
{
Base *p = new Base();
delete p;
return 0;
}
01092D70 mov dword ptr [this],ecx
01092D73 mov ecx,dword ptr [this]
//静态绑定
01092D76 call Base::~Base (01091578h)