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

C++虚函数

2015-09-13 19:35 387 查看
转自:/article/2551883.html

测试代码:

class Base {

public:

virtual void f() { cout << "Base::f" << endl; }

virtual void g() { cout << "Base::g" << endl; }

virtual void h() { cout << "Base::h" << endl; }

};

Base b;
Fun pFun = NULL;
cout << "虚函数表地址:" << (int*)(&b) << endl;
cout << "虚函数表 — 第一个函数地址:" << (int*)*(int*)(&b) << endl;
// Invoke the first virtual function
pFun = (Fun)*((long long*)*(long long*)(&b));
pFun();

pFun = (Fun)*((long long*)*(long long*)(&b) + 1);
pFun();

pFun = (Fun)*((long long*)*(long long*)(&b)+2);
pFun();


其中:*(long long*)(&b)是把对象b最前面8字节内容取出;((long long*)*(long long*)(&b)),把取出的内容看作地址,也就是虚函数表的开始地址,即Base::f()所在位置;*((long long*)*(long long*)(&b))把虚函数表第一个位置内容取出;(Fun)*((long long*)*(long long*)(&b)内容作为地址,赋给函数指针。

结果:

虚函数表地址:0000006A8A65FB78

虚函数表 — 第一个函数地址:000000004CEA0498

Base::f

Base::g

Base::h



其它成员指的是member,不包括member function。成员函数存放在代码区域,同过对象空间内的指针调用。

注意:在上面这个图中,我在虚函数表的最后多加了一个结点,这是虚函数表的结束结点,就像字符串的结束符“/0”一样,其标志了虚函数表的结束。这个结束标志的值在不同的编译器下是不同的。

一般继承(无虚函数覆盖)



我们可以看到下面两点:

虚函数按照其声明顺序放于表中。

父类的虚函数在子类的虚函数前面。

一般继承(有虚函数覆盖)



我们从表中可以看到下面两点:

覆盖的f()函数被放到了虚表中原来父类虚函数的位置。

没有被覆盖的函数依旧。

多重继承(没有虚函数覆盖)

假设有下面这样一个类的继承关系。注意:子类并没有覆盖父类的函数。





我们可以看到:

每个父类都有自己的虚表。

子类的成员函数被放到了第一个父类的表中。(所谓的第一个父类是按照声明顺序来判断的)

这样做就是为了解决不同的父类类型的指针指向同一个子类实例,而能够调用到实际的函数。

多重继承(有虚函数覆盖)





我们可以看见,三个父类虚函数表中的f()的位置被替换成了子类的函数指针。这样,我们就可以任一静态类型的父类来指向子类,并调用子类的f()了。如:

Derive d;
Base1 *b1 = &d;
Base2 *b2 = &d;
Base3 *b3 = &d;

b1->f(); //Derive::f()
b2->f(); //Derive::f()
b3->f(); //Derive::f()

b1->g(); //Base1::g()
b2->g(); //Base2::g()
b3->g(); //Base3::g()


long long** p = (long long**)&b;
pFun = (Fun)p[0][0];
pFun();
pFun = (Fun)p[0][1];
pFun();
pFun = (Fun)p[0][2];
pFun();


此处注意数组指针和二级指针的区别。

注:如果基类的析构函数声明为虚函数,那么在虚函数表base1中,Deriver::g1前会有base1析构函数的地址;在vs的虚函数表中base1析构函数后的地址不会显示,但确实存在。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: