虚函数及继承

转载 2013年12月02日 14:22:23
1、空类,空类单继承,空类多继承的sizeof
  1. #include <iostream>
  2. using namespace std;

  3. class Base1
  4. {

  5. };

  6. class Base2
  7. {

  8. };

  9. class Derived1:public Base1
  10. {

  11. };

  12. class Derived2:public Base1, public Base2
  13. {

  14. };

  15. int main()
  16. {
  17.     Base1 b1;
  18.     Base2 b2;
  19.     Derived1 d1;
  20.     Derived2 d2;
  21.     cout<<"sizeof(Base1) = "<<sizeof(Base1)<<" sizeof(b1) = "<<sizeof(b1)<<endl;
  22.      cout<<"sizeof(Base2) = "<<sizeof(Base2)<<" sizeof(b2) = "<<sizeof(b2)<<endl;
  23.     cout<<"sizeof(Derived1) = "<<sizeof(Derived1)<<" sizeof(d1) = "<<sizeof(d1)<<endl;
  24.     cout<<"sizeof(Derived2) = "<<sizeof(Derived2)<<" sizeof(d1) = "<<sizeof(d1)<<endl;
  25.   
  26.     return 0;
  27. }
结果为:
sizeof(Base1) = 1 sizeof(b1) = 1
sizeof(Base2) = 1 sizeof(b2) = 1
sizeof(Derived1) = 1 sizeof(d1) = 1
sizeof(Derived2) = 1 sizeof(d1) = 1
可以看出所有的结果都是1。
 
2、含有虚函数的类以及虚继承类的sizeof
虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。编译器必需要保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证正确取到虚函数的偏移量)。

假设我们有这样的一个类:

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时,其b中成员的存放如下:

指向虚函数表的指针在对象b的最前面。

虚函数表的最后多加了一个结点,这是虚函数表的结束结点,就像字符串的结束符“\0”一样,其标志了虚函数表的结束。这个结束标志的值在不同的编译器下是不同的。在WinXP+VS2003下,这个值是NULL。而在Ubuntu 7.10 + Linux 2.6.22 + GCC 4.1.3下,这个值是如果1,表示还有下一个虚函数表,如果值是0,表示是最后一个虚函数表。

因为对象b中多了一个指向虚函数表的指针,而指针的sizeof是4,因此含有虚函数的类或实例最后的sizeof是实际的数据成员的sizeof加4。

下面将讨论针对基类含有虚函数的继承讨论

(1)在派生类中不对基类的虚函数进行覆盖,同时派生类中还拥有自己的虚函数,比如有如下的派生类:

class Derived: public Base

 {

public:

virtual void f1() { cout << "Derived::f1" << endl; }

virtual void g1() { cout << "Derived::g1" << endl; }

virtual void h1() { cout << "Derived::h1" << endl; }

};

基类和派生类的关系如下:

当定义一个Derived的对象d后,其成员的存放如下:

可以发现:

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

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

此时基类和派生类的sizeof都是数据成员的sizeof加4。

(2)在派生类中对基类的虚函数进行覆盖,假设有如下的派生类:

class Derived: public Base

 {

public:

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

virtual void g1() { cout << "Derived::g1" << endl; }

virtual void h1() { cout << "Derived::h1" << endl; }

};

基类和派生类之间的关系:其中基类的虚函数f在派生类中被覆盖了

当我们定义一个派生类对象d后,其d的成员存放为:

可以发现:

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

2)没有被覆盖的函数依旧。

这样,我们就可以看到对于下面这样的程序,

Base *b = new Derive();

b->f();

由b所指的内存中的虚函数表的f()的位置已经被Derive::f()函数地址所取代,于是在实际调用发生时,是Derive::f()被调用了。这就实现了多态。

(3)多继承:无虚函数覆盖

假设基类和派生类之间有如下关系:

对于子类实例中的虚函数表,是下面这个样子:

我们可以看到:

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

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

由于每个基类都需要一个指针来指向其虚函数表,因此d的sizeof等于d的数据成员加3*4=12。

(4)多重继承,含虚函数覆盖

假设,基类和派生类又如下关系:派生类中覆盖了基类的虚函数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()

3、一个关于含虚函数及虚继承的sizeof计算

  1. #include <iostream>
  2. using namespace std;

  3. class Base
  4. {
  5. public:
  6.     virtual void f();
  7.     virtual void g();
  8.     virtual void h();
  9. };

  10. class Derived1: public Base
  11. {
  12. public:
  13.     virtual void f1();
  14.     virtual void g1();
  15.     virtual void h1();
  16. };

  17. class Derived2:public Base
  18. {
  19. public:
  20.     virtual void f();
  21.     virtual void g1();
  22.     virtual void h1();
  23. };

  24. class Derived3:virtual public Base
  25. {
  26. public:
  27.     virtual void f1();
  28.     virtual void g1();
  29.     virtual void h1();
  30. };

  31. class Derived4:virtual public Base
  32. {
  33. public:
  34.     virtual void f();
  35.     virtual void g1();
  36.     virtual void h1();
  37. };

  38. class Derived5:virtual public Base
  39. {
  40. public:
  41.     virtual void f();
  42.     virtual void g();
  43.     virtual void h();
  44. };

  45. class Derived6:virtual public Base
  46. {

  47. };

  48. int main()
  49. {
  50.     cout<<sizeof(Base)<<endl; //4
  51.     cout<<sizeof(Derived1)<<endl; //4
  52.     cout<<sizeof(Derived2)<<endl; //4
  53.     cout<<sizeof(Derived3)<<endl; //12
  54.     cout<<sizeof(Derived4)<<endl; //12
  55.     cout<<sizeof(Derived5)<<endl; //8
  56.     cout<<sizeof(Derived6)<<endl; //8

  57.     return 0;
  58. }
对于Base, Derived1和Derived2的结果根据前面关于继承的分析是比较好理解的,不过对于虚继承的方式则有点不一样了,根据结果自己得出的一种关于虚继承的分析,如对Derived3或Derived4定义一个对象d,其里面会出现三个跟虚函数以及虚继承的指针,因为是虚继承,因此引入一个指针指向虚继承的基类,第二由于在基类中有虚函数,因此需要指针指向其虚函数表,由于派生类自己本身也有自己的虚函数,因为采取的是虚继承,因此它自己的虚函数不会放到基类的虚函数表的后面,而是另外分配一个只存放自己的虚函数的虚函数表,于是又引入一个指针,从例子中看到Derived5和Derived6的结果是8,原因是在派生类要么没有自己的虚函数,要么全部都是对基类虚函数的覆盖,因此就少了指向其派生类自己的虚函数表的指针,故结果要少4。(这个是个人的分析,但原理不知道是不是这样的)

C++继承,虚函数与多态性专题.

  • 2012年05月05日 19:35
  • 333KB
  • 下载

C++ 虚函数;重载与覆盖;虚继承

1.什么是虚函数 简单地说,那些被virtual关键字修饰的成员函数,就是虚函数。虚函数的作用,用专业术语来解释就是实现多态性(Polymorphism),多态性是将接口与实现进行分离;用形象的...

C++继承,虚函数与多态性专题

  • 2010年08月04日 08:41
  • 325KB
  • 下载

类继承时的虚函数调用关系

#include using namespace std; class A { public: virtual void func(); virtual int operator=(int n...

继承,虚函数,抽象类

  • 2012年10月11日 11:31
  • 27KB
  • 下载

子类继承父类的虚函数调用

子类继承父类中,虚函数再构造函数中和不再构造函数中的调用

关于C++多重继承的时候虚函数覆盖的问题

问题描述: 下面的代码试图去通过运行时程序分析和验证C++的虚表实现机制。 下面的代码在探究多重继承情况下,虚表的情况 copy代码, g++编译,观察输出结果,会发现,在派生类@Der...

C++类对应的内存结构(虚函数,虚继承)

转自:http://blog.csdn.net/guogangj/article/details/2036785   提示1:对“内存结构”表示有疑问或不解的,先参考: http://blog....
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:虚函数及继承
举报原因:
原因补充:

(最多只允许输入30个字)