C++学习之菱形继承对象的结构分析

最近学习C++,知道在菱形继承关系中虚继承的出现可以解决派生类数据冗余问题,那么虚拟继承后的派生类是什么样子呢?

1.首先说一下什么是菱形继承的二义性问题

引用Siddhartha Rao在<21天学通c++>第八版11.2节的话:

"在继承层次结构中,继承多个从同一个类派生而来的基类时,如果这些基类没有采用虚
继承,将导致二义性。这种二义性被称为菱形问题(Diamond Problem)"

"C++关键字 virtual 的含义随上下文而异(我想这样做的目的很可能是为了省事),对其含
义总结如下:
在函数声明中, virtual 意味着当基类指针指向派生对象时,通过它可调用派生类的相应
函数。
从 Base 类派生出 Derived1 和 Derived2 类时,如果使用了关键字 virtual,则意味着再从
Derived1 和 Derived2 派生出 Derived3 时,每个 Derived3 实例只包含一个 Base 实例。
也就是说,关键字 virtual 被用于实现两个不同的概念"

介绍一篇讲得比较好的虚拟继承原理的文章, 其中虚基类表的内容我在linux ubuntu上用gdb命令没有看出什么结果来,日后若有知晓回头补充

【转】https://www.2cto.com/kf/201604/498612.html

【转】https://blog.csdn.net/melody_1016/article/details/52966355

2.自己写代码验证上述结论并看清楚虚继承下对象的内存结构

基类:Base,两个子类KidOne,KidTwo虚继承于Base,并派生出Tuna

根据打印结果我们可以看到实际上Tuna对象实质上只有一个Base对象,而KidOne,KidTwo中多了虚基类表指针vbptr(当然这是编译器行为, 不同编译器可能对虚继承有不同的处理, linux Ubuntu的原理目前没有搞清楚,暂且先这样理解)

(1)构造顺序

Base,KidOne,KidTwo,Tuna

需要注意的是&myTuna的地址是0xbfab3b44,而Base对象的地址是0xbfab3b58

所以在内存中实际的排布是(从低地址->高地址)

vbptr(KidOne)->kidone->vbptr(KidTwo)->kidtwo->tuna->base

(2)结构分析

因为在32位系统下,所以指针大小为4,在公司64位系统试过指针大小为8

可以看出myTuna对象中实际只有一个Base对象,实际上这个Base被KidOne与KidTwo共享了

root@ubuntu:/lianxi/lianxi_c++/duotai# ./b.out
call Base constructor, this = 0xbfab3b58
call KidOne constructor, this = 0xbfab3b44
call KidTwo constructor, this = 0xbfab3b4c
call Tuna constructor, this = 0xbfab3b44
&myTuna = 0xbfab3b44

=====SIZEOF TEST=====
sizeof(Base)   = 4
sizeof(KidOne) = 12
sizeof(KidTwo) = 12
sizeof(Tuna)   = 24

=====STRUCTURE TEST=====
Please verify, p = 0xbfab3b44
*p       = 0x80491b4
*(p + 1) = 1
*(p + 2) = 0x80491c0
*(p + 3) = 2
*(p + 4) = 3
*(p + 5) = 0
call Tuna destroyer, this = 0xbfab3b44
call KidTwo destroyer, this = 0xbfab3b4c
call KidOne destroyer, this = 0xbfab3b44
call Base destroyer, this = 0xbfab3b58
root@ubuntu:/lianxi/lianxi_c++/duotai# 
/*Name   :virtual_inherit_sample.cpp
 *Purpose:describe the structure of virtual inherit object
 *Date   :2018-12-15
 */
 #include <iostream>
 #include <stdio.h>
 using namespace std;

 class Base
 {
    private:
        int base;
    public:
        /*constructor*/
        Base(int baseInit = 0) : base(baseInit)
        {
            cout << "call Base constructor, this = " << this << endl;
        }
        /*destroyer*/
        ~Base(void)
        {
            cout << "call Base destroyer, this = " << this << endl;
        }
 };/*class Base*/

 class KidOne : virtual public Base
 {
    private:
        int kidone;
    public:
        /*constructor*/
        KidOne(int kidoneInit = 1) : kidone(kidoneInit)
        {
            cout << "call KidOne constructor, this = " << this << endl;
        }
        /*destroyer*/
        ~KidOne(void)
        {
            cout << "call KidOne destroyer, this = " << this << endl;
        }
 };/*class KidOne*/

  class KidTwo : virtual public Base
 {
    private:
        int kidtwo;
    public:
        /*constructor*/
        KidTwo(int kidtwoInit = 2) : kidtwo(kidtwoInit)
        {
            cout << "call KidTwo constructor, this = " << this << endl;
        }
        /*destroyer*/
        ~KidTwo(void)
        {
            cout << "call KidTwo destroyer, this = " << this << endl;
        }
 };/*class KidTwo*/

 class Tuna : public KidOne, public KidTwo
 {
    private:
        int tuna;
    public:
        /*constructor*/
        Tuna(int tunaInit = 3) : tuna(tunaInit)
        {
            cout << "call Tuna constructor, this = " << this << endl;
        }
        /*destroyer*/
        ~Tuna(void)
        {
            cout << "call Tuna destroyer, this = " << this << endl;
        }
 };/*class Tuna*/

 /*test on 32bit OS*/
int main(void)
{
    Tuna myTuna;
    int* p = NULL;
    cout << "&myTuna = " << &myTuna << endl;
    putchar(10);
    
    printf("=====SIZEOF TEST=====\n");
    printf("sizeof(Base)   = %d\n", sizeof(Base));   /*4*/
    printf("sizeof(KidOne) = %d\n", sizeof(KidOne));/*12*/
    printf("sizeof(KidTwo) = %d\n", sizeof(KidTwo));/*12*/
    printf("sizeof(Tuna)   = %d\n", sizeof(Tuna));  /*24*/
    putchar(10);

    printf("=====STRUCTURE TEST=====\n");
    p = (int*)(&myTuna);
    printf("Please verify, p = %p\n", p);
    printf("*p       = %p\n", *p);
    printf("*(p + 1) = %d\n", *(p + 1));/*1*/
    printf("*(p + 2) = %p\n", *(p + 2));
    printf("*(p + 3) = %d\n", *(p + 3));/*2*/
    printf("*(p + 4) = %d\n", *(p + 4));/*3*/
    printf("*(p + 5) = %d\n", *(p + 5));/*0*/

    return (0);
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值