C++【侯捷】——— 虚指针和虚表动态绑定
虚函数的两种用法
继承和组合中构造和析构,可以通过内存的角度进行分析。
继承关系是构造父类被包含在子类中,所以构造必须由内而外,析构方向相反。
组合关系是拥有的关系,构造函数也是由内而外,析构方向相反。
继承和组合的构造/析构函数后面编译器会加上父类的构造函数:
Container::Container():Component() { … }
继承和组合同时存在的情况下,父类和组合都存在与子类中,两个的前后关系没有规定,构造也由内而外,析构相反。先父类后组合,析构是先组合再父类。
虚指针和虚表
只有类中有虚函数,其内存对象中就会多一个指针,不管虚函数有多少个,都只有一个。子类的内存对象中有父类的part。有虚函数也是同样的关系。
从类的角度,继承会把父类的内存和成员继承下来,函数继承是继承它的调用权。
函数是一块代码,再内存中是一块,在类中虚函数会生成一个指针表,每个指针指向对应的函数段。如果子类中重写了父类的虚函数,虽然子类的虚函数表中对应函数指针会指向重写的函数地址,但是,不代表父类的旧的函数段会被覆盖,它还是在的。前面的类名不同了。
class A {
public:
virtual void vfunc1();
virtual void vfunc2();
void func1();
void func2();
}
class B {
public:
virtual void vfunc1();
void func1();
}
class C {
public:
virtual void vfunc2();
void func2();
}
```cpp
虚函数地址会有:
```cpp
A::vfunc1() //1
A::vfunc2() //2
B::vfunc1() //3
C::vfunc2() //4
A对象的虚函数表中有两个指针,分别指向1和2
B对象的虚函数表中有两个指针,分别指向2和3
C对象的虚函数表中有两个指针,分别指向3和4
在C语言中编译器通过静态绑定的方法进行,将当前的地址保存起来,调用CALL指令跳转到调用的函数中,处理完成后再返回来。而虚函数是通过动态绑定的方法,通过对象中定的虚函数表指针找到虚函数表,再从虚函数表中找到对应的虚函数指针,然后调用起这个函数。
动态绑定(虚机制)的实现和条件:
1、必须是指针
2、指针是向上指定的,类型是父类,可以new成子类对象
3、必须是虚函数
静态绑定一定指向某个固定的地址,动态要根据具体来判断。这种动态绑定的方法就叫做多态。删改你的概念其实都是同一个概念,走的是虚指针和虚表。
this pointer
Tempate Method
父类把所有能实现的方法都实现了,生一个Serialize不知道怎么实现,留给子类来进行实现。调用的时候动态绑定,OnFileOpen方法中编译器会传一个this指针,这个指针指向的是子类的对象,这个子类可以向上转型。同事
CDocument:
OnFileOpen()
{
…
Serialize();
…
}
virtual Serialize();
class CMyDoc: pulbic CDocument
{
virtual Serialize() {…}
}
main()
{
CMyDoc myDoc;
…
myDoc.OnFileOpen();
}