继承关系中虚函数及其默认值 | 动态联编。

静态联编:指联编工作出现在编译连接阶段,这种联编又称早期联编,它解决了程序中的操作调用与执行该操作代码间的关系。

动态联编:程序在编译阶段并不能确切知道将要调用的函数,只有在程序运行时才能确定将要调用的函数(动多态),为此要确切知道该调用的函数,要求联编工作要在程序运行时进行,这种在程序运行时进行联编工作被称为动态联编。动态联编又称动态关联、滞后联编。

原理是通过基类指针(或引用)派生类对象,在运行时通过虚函数表查询所调用的函数。

首先,需要明确的时,函数参数的缺省值是在编译期就确定的,在编译期如果函数调用中的某个参数缺省,编译器就会使用函数的默认值作为参数调用。

而C++中的动多态是运行时才会确定调用哪一个函数。 而如果发生动多态的函数具有默认值的话,它的调用又是怎样的呢?

参考如下代码:基类Object中,虚函数print有缺省参数int a = 10; ,同样的在派生类Base中,虚函数print也有缺省参数int b = 100; 。当我在主函数main中,尝试通过动多态的方式调用print(),最后的输出结果又是怎样呢?

class Object {
public:
	virtual void print(int a = 10) {
		cout << "Object : : printf a: " << a << endl;
	}
};
class Base :public Object{
private:
virtual void print(int b = 100) {
cout << "Base : : print b: " << b << endl;
}
};

int main()
{
	Base b1;
	Object& pobj = b1;	// Object* aobj = &b1;
	pobj.print();		// aobj->print();

	return 0;
}

输出结果:

Base : : print b: 10

此外,我们还注意到了在上述的代码中,Base类的虚print函数是私有访问限定下的类成员函数,可是程序结果显示它仍然可以调用。

分析:

当main函数在编译时,编译到此代码:

  • Object& pobj = b1;
    pobj.print();

由于pobj类型是Object类型,在Object中print()是公有访问的,因此编译阶段不会报错。此外,编译阶段会产生汇编指令,把pobj.print();这个函数调用翻译成汇编指令的过程中,记录了参数的值(由于我们没有传参,这里压入栈的是函数的缺省值),由于pobj的类型是Object类型,因此,在编译期确定的函数的参数是 a = 10 ,而具体的函数调用此时并不明确,需要在运行时才能确定。

(使用vs 2019 反汇编此代码,可以看到执行call指令之前执行了参数入栈的执行push 0Ah )
在这里插入图片描述
而在函数运行阶段,在执行到此语句时:

  • Object& pobj = b1;
    pobj.print();

在调用print时,通过pobj内部的虚表指针定位到对象的虚表,其中虚表中RTTI保存这运行时类型信息,而在虚表中保存的虚函数入口地址正是此次调用的关键。我们都知道通过继承实现的虚函数,派生类的虚表中自身的虚函数地址会覆盖掉基类的虚函数地址。因此,这里调用的是Base::print() 函数。

最后的执行结果为 Base : : print b: 10

而说回Base中print()的访问权限的问题,因为 Base::print() 是在Base类的私有访问权限下的,因此我们通过对象调用 print 方法坑定是无法通过编译的。


最后,说道函数的默认值与虚函数,不得不说他们语法上有个共同点需要我们注意:

  • 如果定义放在类外,参数的默认值一般在声明时指定,而在实现(定义)时不能(再)加默认值
  • 如果定义放在类外,virtual只能加在声明处,在类外实现(定义)时不能(再)加virturl

因此,我们应尽量避免在虚函数中使用默认值,以免某些时候在程序中会发生一些即出人意料,又在情理之中的结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我叫RT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值