动态绑定什么时候发生?所有的工作都是由编译器在幕后完成。当我们告诉通过创建一个virtual函数来告诉编译器要进行动态绑定,那么编译器就会根据动态绑定机制来实现我们的要求, 不会再执行早绑定。
问题:C++的动态捆绑机制是怎么样的?
首先,我们看看编译器如何处理虚函数。当编译器发现我们的类中有虚函数的时候,编译器会创建一张虚函数表,把虚函数的函数入口地址放到虚函数表中,并且在类中秘密增加一个指针,这个指针就是vpointer(缩写vptr),这个指针是指向对象的虚函数表。在多态调用的时候,根据vptr指针,找到虚函数表来实现动态绑定。
验证对象中的虚指针:
class A{ public: virtual void func1(){} virtual void func2(){} }; //B类为空,那么大小应该是1字节,实际情况是这样吗? class B : public A{}; void test(){ cout << "A size:" << sizeof(A) << endl; cout << "B size:" << sizeof(B) << endl; }
在编译阶段,编译器秘密增加了一个vptr指针,但是此时vptr指针并没有初始化指向虚函数表(vtable),什么时候vptr才会指向虚函数表?在对象构建的时候,也就是在对象初始化调用构造函数的时候。编译器首先默认会在我们所编写的每一个构造函数中,增加一些vptr指针初始化的代码。如果没有提供构造函数,编译器会提供默认的构造函数,那么就会在默认构造函数里做此项工作,初始化vptr指针,使之指向本对象的虚函数表。
起初,子类继承基类,子类继承了基类的vptr指针,这个vptr指针是指向基类虚函数表,当子类调用构造函数,使得子类的vptr指针指向了子类的虚函数表。
1.当子类无重写基类虚函数时:
过程分析
:
Animal* animal = new Dog; animal->fun1();
当程序执行到这里,会去animal指向的空间中寻找vptr指针,通过vptr指针找到func1函数,此时由于子类并没有重写也就是覆盖基类的func1函数,所以调用func1时,仍然调用的是基类的func1.
执行结果
: 我是基类的func1
测试结论
: 无重写基类的虚函数,无意义
2.当子类重写基类虚函数时:
过程分析
:
Animal* animal = new Dog; animal->fun1();
当程序执行到这里,会去animal指向的空间中寻找vptr指针,通过vptr指针找到func1函数,由于子类重写基类的func1函数,所以调用func1时,调用的是子类的func1.
执行结果
: 我是子类的func1
测试结论
: 无重写基类的虚函数,无意义
多态的成立条件:
- 有继承
- 子类重写父类虚函数函数
a) 返回值,函数名字,函数参数,必须和父类完全一致(析构函数除外)
b) 子类中virtual关键字可写可不写,建议写 - 类型兼容,父类指针,父类引用 指向 子类对象