C++中的继承特性(2)

来源:互联网 发布:七雄战记更新网络错误 编辑:程序博客网 时间:2024/05/31 15:18

4.static成员与继承
在类中,无论创建了多少个对象,被static修饰的成员变量永远只有一个,那么派生类是否也继承了这个静态成员变量呢?
假设一:派生类继承了这个变量,且与基类共享同一个静态成员;
假设二:派生类拷贝了这个变量,不与基类共享,是派生类所有对象共有的static静态成员。
这一点其实很好证明。由上一篇博文我们基本理解了派生类的构造函数和析构函数,那么这里就利用构造函数和析构函数来验证哪一种假设正确。

我们来看下面一段代码:

class A{public:    A(){        ++_a;        cout << "A: _a = " << _a << endl;    }    ~A(){        --_a;        cout << "~A: _a = " << _a << endl;    }    static int _a;};int A::_a = 0;class B :public A{public:    B(){        cout << "B: _a = " << _a << endl;    }    ~B(){        cout << "~B: _a = " << _a << endl;    }};void FunTest(){    A a;    B b;}int main(){    FunTest();    system("pause");    return 0;}

阅读上段代码,分析可得:
<1>在假设一下,创建对象a时使静态成员变量_ a自增,创建对象b时将在初始化列表中调用A(),使_ a自增,此时_ a为2;销毁对象b时先调用~B(),再调用~A()使_ a自减,此时_ a为1,销毁对象a时再次使_ a自减,最终为零,故此时应在屏幕上输出如下结果:

A: _a = 1A: _a = 2B: _a = 2~B: _a = 1~A: _a = 1~A: _a = 0

<2>在假设二下,创建对象a时使A::_ a自增至1,创建对象b时调用A()使B类自己的静态成员_ a自增至1;销毁对象b时先调用~B(),再调用~A()使B类自己的静态成员_ a自减至0,销毁对象a时使A::_ a自减至0,此时应在屏幕上输出如下结果:

A: _a = 1A: _a = 1B: _a = 1~B(): _a = 1~A: _a = 1~A: _a = 0

而后在VS中运行上述程序代码,得到结果如下图:
static与继承
验证假设一正确!即所有的派生类与基类共享相同的static静态成员变量。


5.继承的分类
1)单继承
一个子类只有一个直接父类称为单继承。单继承结构简单且易于理解,示意图如下:
单继承
2)多继承
一个子类有多个直接父类称为多继承,示意图如下:
多继承


6.虚继承
在多继承中,会出现如下一种继承方式——菱形继承:
菱形继承
我们通过如下一段代码观察一个Three对象中各个变量的存放:

class Zero{public:    int zero;};class One :public Zero{public:    int one;};class Two :public Zero{public:    int two;};class Three :public One, public Two{public:    int three;};void FunTest(){    Three t;    t.One::zero = 0;    t.one = 1;    t.Two::zero = 2;    t.two = 3;    t.three = 4;}int main(){    FunTest();    system("pause");    return 0;}

代码执行的内存截图如下:
菱形继承
首先从内存视图中我们可以看到:1.类中的成员变量存放次序是先父类,后本类;2.若父类不只一个,将按继承的顺序依次存放。
其次,很明显这样会使得一个Three的对象中存在两个Zero的对象,如果我们的本意不是这样,将造成数据冗余和空间浪费。为解决此问题,C++出现了虚拟继承——即将共同基类设置为虚基类,当出现了共同基类时,就只有一个基类对象,同名数据成员仅有一份拷贝,同一个函数名也只有一个映射,解决了二义性问题、节省了内存空间、避免了数据不一致的问题(参考http://blog.csdn.net/crystal_avast/article/details/7678704)。
虚继承格式为: class <派生类类名> :virtual 继承方式 <基类名>
那么同样,我们来研究一下虚继承情况下上述Three类对象的内存空间,修改后的代码如下:

class Zero{public:    int zero;};class One :virtual public Zero{public:    int one;};class Two :virtual public Zero{public:    int two;};class Three :public One, public Two{public:    int three;};void FunTest(){    Three t;    t.One::zero = 0;    t.one = 1;    t.Two::zero = 2;    t.two = 3;    t.zero = 0;    t.three = 4;}int main(){    cout << sizeof(Three) << endl;    FunTest();    system("pause");    return 0;}

调试上述代码,首先得到Three这个类的大小为24Byte,在执行FunTest函数后观察内存可以看到:
虚继承内存
分析此时的内存布局,是如下情况:
内存布局1
再分别进入两个地址,得到0110e0a4所指向的空间如下图,发现前四个字节为0,后四个字节值为20,正好是t中one类对象与zero对象地址的差值:
内存2
得到0110e1a8所指向的空间如下,同理得到后四个字节值是two对象与zero对象地址的差值:
内存3
故而对于有两个基类的虚继承,内存布局如下图所示:
内存布局
通过查阅其它博客(http://blog.chinaunix.net/uid-16723279-id-3568748.html)关于虚继承的讨论,得到在虚继承的情况下内存空间分布为:
1、B1的虚函数表(子类重定义的基类的虚函数、重定义的B1的虚函数、子类自己的虚函数)
2、继承自B1的数据成员
3、B2的虚函数表(子类重定义的基类的数据成员、重定义的B2的虚函数)
4、继承自B2的数据成员
5、子类自己的数据成员
6、基类的虚函数表(子类重定义的虚函数、基类的虚函数)
7、基类的数据成员

以上仅为博主初学认知,如有错误或不当之处,欢迎批评指正!

0 0
原创粉丝点击