编译器会构建一张虚表( vtable ),每一个类都有自己独特的虚表。同时,在这个继承链上,编译器会为基类插入一个隐式的指针(一般是对象的首地址),指向虚表,称为__vptr。然后,子类继承父类时,会获得继承下来的__vptr,再根据自己的类的情况兼容(修改虚函数表里的值、发生偏移等。于是,当我们构建具体的类时,若是基类类型,__vptr就会指向父类的vtable,若是子类类型,__vptr就会指向子类的vtable。
不同的类型,__vptr指向的vtable不同
#include
using namespace std;
class A {
public:
virtual void f() {
cout << "A::f()" << endl;
}
virtual void g() {
cout << "A::g()" << endl;
}
};
class B :public A {
public:
virtual void f() {
cout << "B::f()" << endl;
}
virtual void h() {
cout << "B::h()" << endl;
}
};
int main()
{
A a;
B b;
return 0;
}
父类的函数为virtual时,子类继承下来的这个函数也是虚函数,这被称为覆写。以前我们一般会推荐大家在子类也为这个函数标记为virtual,提醒我们这是虚函数,而在C++11中,我们更提倡使用override来提醒我们在覆写父类的虚函数,上面代码中,子类b继承父类a的__vptr,同时根据自己类的情况做出兼容,将_vptr所指向的虚函数表(vtable)覆盖,再在虚函数表的后面添加自己的虚函数(virtual void h()
)。
如果是下面的情况,即子类的第二个虚函数override
了父类的第一个虚函数。
#include
using namespace std;
class A {
public:
virtual void g() {
cout << "A::g()" << endl;
}
virtual void h() {
cout << "A::h()" << endl;
}
};
class B :public A {
public:
virtual void f() {
cout << "B::f()" << endl;
}
virtual void g() {
cout << "B::g()" << endl;
}
};
int main()
{
A a;
B b;
return 0;
}
则,会先获得指针的偏移量以保证vptr的正确性。
#include
using namespace std;
class Test
{
public:
Test(int a) { data = a; }
virtual ~Test() { cout << "Test deconstruct" << endl; }//基类中的虚析构函数
virtual void fun11() { cout << "Test virtual fun11" << endl; }//基类中的虚函数fun11
virtual void fun12() { cout << "Test virtual fun12" << endl; }//基类中的虚函数fun12
int data;
};
class Test1 :public Test
{
public:
Test1(int d1, int d2) :Test(d2) { data1 = d1; data2 = d2; }
int data1;
int data2;
virtual ~Test1() { cout << "test1 deconstruct" << endl; }//派生类中的虚析构函数
virtual void fun1() { cout << "test1 virtual fun1" << endl; }//派生类中的虚函数fun1,不是实现基类中的fun11的多态
virtual void fun2() { cout << "test1 virtual fun2" << endl; }//派生类中的虚函数fun2,不是实现基类中的fun12的多态
};
typedef void(*Fun)(void);//指向函数的指针
int main()
{
Test objt(10); //父类对象
Test1 obj(1, 2);//定义子类对象obj
cout << "obj's Size = " << sizeof(obj) << endl;
cout << "obj 's Address = " << &obj << endl;
cout << "second Test1 object's address = " << &obj + 1 << endl;//为了测试(int*)(&obj+1)和((int*)&obj+1)的区别
cout << "third Test1 object's address = " << &obj + 2 << endl;
cout << "base data address and data: " << (int*)&obj + 1 << "\t" << *((int*)&obj + 1) << endl;
cout << "derivate data1 address and data1:" << (int*)&obj + 2 << "\t" << *((int*)&obj + 2) << endl;
cout << "derivate data2 address and data2:" << (int*)&obj + 3 << "\t" << *((int*)&obj + 3) << endl;
//获得虚表指针,显示虚表中的内容
cout << "vtable address = " << (int*)&obj << "\t" << "value = " << *((int*)&obj + 0) << endl;
cout << "vtable value0 的地址 = " << ((int*)*(int*)((int*)&obj + 0) + 0) << endl;
cout << "vtable value1 的地址 = " << ((int*)*(int*)((int*)&obj + 0) + 1) << endl;
cout << "vtable value2 的地址 = " << ((int*)*(int*)((int*)&obj + 0) + 2) << endl;
cout << "vtable value3 的地址 = " << ((int*)*(int*)((int*)&obj + 0) + 3) << endl;
cout << "vtable value4 的地址 = " << ((int*)*(int*)((int*)&obj + 0) + 4) << endl;
cout << "vtable value5 的地址 = " << ((int*)*(int*)((int*)&obj + 0) + 5) << endl;
Fun pFun = NULL;
pFun = (Fun)*((int*)*(int*)((int*)&obj + 0) + 1);
pFun();
pFun = (Fun)*((int*)*(int*)((int*)&obj + 0) + 2);
pFun();
pFun = (Fun)*((int*)*(int*)((int*)&obj + 0) + 3);
pFun();
pFun = (Fun)*((int*)*(int*)((int*)&obj + 0) + 4);
pFun();
return 0;
}
派生类虚表结构:
vtable |
---|
virtual ~Test() |
virtual void fun11() |
virtual void fun12() |
virtual void fun1() |
virtual void fun2() |
现在更改析构函数:基类虚析构函数修改成虚构函数,子类虚析构函数
则我们有:
#include
using namespace std;
class Test
{
public:
Test(int a) { data = a; }
~Test() { cout << "Test deconstruct" << endl; }//基类中的析构函数
virtual void fun11() { cout << "Test virtual fun11" << endl; }//基类中的虚函数fun11
virtual void fun12() { cout << "Test virtual fun12" << endl; }//基类中的虚函数fun12
int data;
};
class Test1 :public Test
{
public:
Test1(int d1, int d2) :Test(d2) { data1 = d1; data2 = d2; }
int data1;
int data2;
virtual ~Test1() { cout << "test1 deconstruct" << endl; }//派生类中的虚析构函数
virtual void fun1() { cout << "test1 virtual fun1" << endl; }//派生类中的虚函数fun1,不是实现基类中的fun11的多态
virtual void fun2() { cout << "test1 virtual fun2" << endl; }//派生类中的虚函数fun2,不是实现基类中的fun12的多态
};
typedef void(*Fun)(void);//指向函数的指针
int main()
{
Test objt(10);
Test1 obj(1, 2);//定义对象obj
cout << "obj's Size = " << sizeof(obj) << endl;
cout << "obj 's Address = " << &obj << endl;
cout << "second Test1 object's address = " << &obj + 1 << endl;//为了测试(int*)(&obj+1)和((int*)&obj+1)的区别
cout << "third Test1 object's address = " << &obj + 2 << endl;
cout << "base data address and data: " << (int*)&obj + 1 << "\t" << *((int*)&obj + 1) << endl;
cout << "derivate data1 address and data1:" << (int*)&obj + 2 << "\t" << *((int*)&obj + 2) << endl;
cout << "derivate data2 address and data2:" << (int*)&obj + 3 << "\t" << *((int*)&obj + 3) << endl;
//获得虚表指针,显示虚表中的内容
cout << "vtable address = " << (int*)&obj << "\t" << "value = " << *((int*)&obj + 0) << endl;
cout << "vtable value0 的地址 = " << ((int*)*(int*)((int*)&obj + 0) + 0) << endl;
cout << "vtable value1 的地址 = " << ((int*)*(int*)((int*)&obj + 0) + 1) << endl;
cout << "vtable value2 的地址 = " << ((int*)*(int*)((int*)&obj + 0) + 2) << endl;
cout << "vtable value3 的地址 = " << ((int*)*(int*)((int*)&obj + 0) + 3) << endl;
cout << "vtable value4 的地址 = " << ((int*)*(int*)((int*)&obj + 0) + 4) << endl;
cout << "vtable value5 的地址 = " << ((int*)*(int*)((int*)&obj + 0) + 5) << endl;
Fun pFun = NULL;
pFun = (Fun)*((int*)*(int*)((int*)&obj + 0) + 0);
pFun();
pFun = (Fun)*((int*)*(int*)((int*)&obj + 0) + 1);
pFun();
pFun = (Fun)*((int*)*(int*)((int*)&obj + 0) + 3);
pFun();
pFun = (Fun)*((int*)*(int*)((int*)&obj + 0) + 4);
pFun();
return 0;
}
vtable |
---|
virtual void fun11() |
virtual void fun12() |
virtual ~Test1() |
virtual void fun1() |
virtual void fun2() |
更改析构函数:基类虚析构函数为虚虚构函数,子类虚析构函数改为析构函数
则我们有:
#include
using namespace std;
class Test
{
public:
Test(int a) { data = a; }
virtual ~Test() { cout << "Test deconstruct" << endl; }//基类中的虚析构函数
virtual void fun11() { cout << "Test virtual fun11" << endl; }//基类中的虚函数fun11
virtual void fun12() { cout << "Test virtual fun12" << endl; }//基类中的虚函数fun12
int data;
};
class Test1 :public Test
{
public:
Test1(int d1, int d2) :Test(d2) { data1 = d1; data2 = d2; }
int data1;
int data2;
~Test1() { cout << "test1 deconstruct" << endl; }//派生类中的析构函数
virtual void fun1() { cout << "test1 virtual fun1" << endl; }//派生类中的虚函数fun1,不是实现基类中的fun11的多态
virtual void fun2() { cout << "test1 virtual fun2" << endl; }//派生类中的虚函数fun2,不是实现基类中的fun12的多态
};
typedef void(*Fun)(void);//指向函数的指针
int main()
{
Test objt(10);
Test1 obj(1, 2);//定义对象obj
cout << "obj's Size = " << sizeof(obj) << endl;
cout << "obj 's Address = " << &obj << endl;
cout << "second Test1 object's address = " << &obj + 1 << endl;//为了测试(int*)(&obj+1)和((int*)&obj+1)的区别
cout << "third Test1 object's address = " << &obj + 2 << endl;
cout << "base data address and data: " << (int*)&obj + 1 << "\t" << *((int*)&obj + 1) << endl;
cout << "derivate data1 address and data1:" << (int*)&obj + 2 << "\t" << *((int*)&obj + 2) << endl;
cout << "derivate data2 address and data2:" << (int*)&obj + 3 << "\t" << *((int*)&obj + 3) << endl;
//获得虚表指针,显示虚表中的内容
cout << "vtable address = " << (int*)&obj << "\t" << "value = " << *((int*)&obj + 0) << endl;
cout << "vtable value0 的地址 = " << ((int*)*(int*)((int*)&obj + 0) + 0) << endl;
cout << "vtable value1 的地址 = " << ((int*)*(int*)((int*)&obj + 0) + 1) << endl;
cout << "vtable value2 的地址 = " << ((int*)*(int*)((int*)&obj + 0) + 2) << endl;
cout << "vtable value3 的地址 = " << ((int*)*(int*)((int*)&obj + 0) + 3) << endl;
cout << "vtable value4 的地址 = " << ((int*)*(int*)((int*)&obj + 0) + 4) << endl;
cout << "vtable value5 的地址 = " << ((int*)*(int*)((int*)&obj + 0) + 5) << endl;
Fun pFun = NULL;
pFun = (Fun)*((int*)*(int*)((int*)&obj + 0) + 1);
pFun();
pFun = (Fun)*((int*)*(int*)((int*)&obj + 0) + 2);
pFun();
pFun = (Fun)*((int*)*(int*)((int*)&obj + 0) + 3);
pFun();
pFun = (Fun)*((int*)*(int*)((int*)&obj + 0) + 4);
pFun();
return 0;
}
派生类虚表结构:
vtable |
---|
virtual ~Test() |
virtual void fun11() |
virtual void fun12() |
virtual void fun1() |
virtual void fun2() |
注意:
有一个值得思考的地方是在子类虚函数中调用已经被override的父类虚函数:
virtual void v_func1()
{
base_class::v_func1();
cout << "This is dev_class's v_func1()" << endl;
}
我们知道,C++比C多了个作用域限定符::
所以,隐藏的不是很深,还是可以揪出来用的
全局函数,变量,类型,enum 常量 被隐藏,可以用 ::引用
名空间内 函数,变量,类型,enum 常量被隐藏,可以用 名空间名:: 引用
类作用域的函数,变量,类型,enum 常量 被隐藏,可以用 类名:: 引用
只有函数 和 函数内部的语句组作用域,名字被隐藏,无法引用
这里你会发现,其实虚函数就是是指这个函数的签名。
而 上面那个虚函数(base_class::v_func1();
)在类中的实现不是,它是一个实实在在的成员函数,也就是和普通的函数一样的。
如果这个函数是虚函数,此时编译器会把其地址放在虚表中。
所以这个函数有两个入口。
第一个就是上面那样呼叫,和普通的成员函数一致。
第二个是通过引用呼叫,此时编译器会通过虚表来呼叫。
此时需要添加一个修正 this 指针的转换函数。
所以,
base_class::v_func1();
这种格式的意思是:不要去虚表找了,直接像普通成员函数一样直接调用基类虚函数。
纯虚函数的原理其实就是:纯虚函数在类的vftable表中对应的表项被赋值为0。也就是指向一个不存在的函数。由于编译器绝对不允许有调用一个不存在的函数的可能,所以该类不能生成对象。在它的派生类中,除非重写此函数,否则也不能生成对象。
纯虚函数的作用:
也就是制定标准,统一接口。
如果一个基类中有多个纯虚函数,即基类为抽象类,那么派生类只有实现所有纯虚函数的override,子类才会是能够实例化的具体类,任何一个父类的纯虚函数没有实现override,则子类也为抽象类不能实例化。