virtual 虚函数补充

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)  
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值