C++ 虚函数的内部实现 (2)

所以在上述代码中,y_ptr指向的是在Z类对象中的子对象,即Y类对象在Z类中函数与变量。

C++ 虚函数的内部实现

注意⚠️ 此时y_ptr中的_vptr指向的是Z类对象的vtable

而y_ptr->printY()这行代码,其实会被编译器翻译成如下伪代码

((y_ptr->_vptr)->_vtbl[2])();

其中y_ptr->_vptr指向Y类对象的vptr指针,vptr指针再指向虚函数表中对应的函数指针项,即((y_ptr->_vptr)->_vtbl[2]), 最后通过函数指针来实现函数调用。

由于这个_vptr指向的是Z类对象的虚函数表,所以调用的printY()函数实际上是Z类中实现的printY(),即输出"printY() in Z"。 动态绑定就这样实现了。

4. 用几个问题加深理解

沿用3中的例子,我们来看接下来的几个问题。

Q1. 如果将Z类对象赋值给Y类变量,动态绑定还会发生吗?

即如下代码中,输出是"printY() in Z"还是"printY() in Y"?

Z zz; Y yy = zz; yy.printY();

答案是不会发生,输出的结果是"printY() in Y"。

首先我们需要明确一个很重要的概念,对_vptr这个指针的赋值操作是在构造类对象的过程中发生的。换一句话说,当一个类的实例被创建的时候_vptr被赋值,指向该类的vtable。一旦类的实例被创建,一个类对象里面的_vptr永远不会变,永远都会指向所属类型的虚函数表。

不论是赋值操作还是赋值构造时, 只会处理成员变量,即把zz中的成员变量赋值给yy, 但是_vptr还是指向Y类的虚函数表。

Q2. 如果在基类中不声明某个函数是虚函数,在子类中重写了这个函数,动态绑定还会发生吗?

即如下代码中,输出是"printX() in Z"还是"printX() in X"?

class X { public: int x; virtual ~X() {} void printX() { cout<<"printX() in X"<<endl; } }; class Z : public X { public: int z; ~Z() {} void printX() { cout<<"printX() in Z"<<endl; } void printZ() { cout<<"printZ() in Z"<<endl; } }; int main(){ X *x_ptr = new Z(); x_ptr->printX(); // OK }

答案是不会发生,输出的结果是"printX() in X"。没有声明为虚函数的函数,不会被放入虚函数表中,即vtable不会保存该函数的函数指针。这时,动态绑定肯定不会发生了。

5. 总结

一般而言,虚函数表是属于一个类的(one vtable per class), 位于静态数据区,而虚函数表指针_vptr是属于一个类的对象的(one vptr per object).

一个由多继承关系的类会有多个虚函数指针。

虚函数指针的赋值操作是在构造类对象的过程中发生的,之后的赋值操作不会改变vptr的值

C++标准没有定义动态绑定的具体实现方式,只是陈述了动态绑定的行为。具体的实现与编译器相关。

以上为个人学习总结,如有错误欢迎指出!

这篇博文参考了如下文章:

https://www.learncpp.com/cpp-tutorial/the-virtual-table/

https://www.cnblogs.com/yinheyi/p/10525543.html

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpffys.html