您的位置:首页 > 编程语言 > C语言/C++

C++虚函数表(vtable)和虚函数指针(vfptr)

2018-01-23 21:54 483 查看
编译器会构建一张虚表( vtable ),每一个类都有自己独特的虚表。同时,在这个继承链上,编译器会为基类插入一个隐式的指针(一般是对象的首地址),指向虚表,称为__vptr。然后,子类继承父类时,会获得继承下来的__vptr,再根据自己的类的情况兼容(修改虚函数表里的值、发生偏移等。于是,当我们构建具体的类时,若是基类类型,__vptr就会指向父类的vtable,若是子类类型,__vptr就会指向子类的vtable。

不同的类型,__vptr指向的vtable不同

#include <iostream>
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()<
4000
/code>)。

如果是下面的情况,即子类的第二个虚函数override
了父类的第一个虚函数。

#include <iostream>
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 <iostream>

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 <iostream>
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 <iostream>
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()
注意:

(int*)(&obj+1)和((int*)&obj+1)的区别是:前面表示的是地址按对象大小增加,后面表示的是一个对象地址按4个字节增加。即(int*)(&obj+1)的位置就是obj的下一个对象的首地址,而((int*)&obj+1)则是对象obj内vptr后面一个成员变量的地址

虚表中存放的是所有虚函数的首地址,存放顺序和(继承顺序、函数的声明顺序)有关。

当派生类中重载了基类中的函数时,(包括析构函数),这时候虚表中就只会有基类中的虚函数的地址,但是当调用派生类中重载的虚函数时,实现的是重载的功能。

有一个值得思考的地方是在子类虚函数中调用已经被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,则子类也为抽象类不能实例化。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: