LiSheng's blog LiSheng's blog
首页
笔记
个人简历
随笔集
GitHub (opens new window)
首页
笔记
个人简历
随笔集
GitHub (opens new window)
  • golang

  • cplus

    • 内存相关

    • 面向对象

      • 相关问题
      • 构造、析构顺序
      • 对象内存布局
      • 静态、动态多态
      • 虚函数表
      • 重载、重写、覆盖
      • 构造函数不能是虚函数
      • CRTP模式的原理和用途
      • 移动语义move
      • 构造函数初始化列表
      • 静态成员变量和函数
      • 多重继承
      • 菱形问题与虚继承
        • 对象池
      • STL相关

      • 内置数据结构
      • 数据结构示例
      • go和c++对比
      • 关键字

    • leetcode

    • 存储技术

    • 分布式系统

    • 计算机网络

    • Linux操作系统

    • Redis

    • 其他

    • 笔记
    • cplus
    • 面向对象
    lisheng
    2024-09-10
    目录

    菱形问题与虚继承

    虚继承(Virtual Inheritance)是C++中的一种技术,用于解决多重继承中的菱形继承问题。在菱形继承结构中,一个类继承了两个类,这两个类又共同继承自同一个基类。虚继承确保派生类只有一个基类的副本,从而解决了重复继承的问题。下面是虚继承的详细实现过程和相关细节:

    # 一、虚继承的基本概念

    虚继承用于确保在多重继承中,对于共享的基类(即共同祖先类),派生类只拥有一个该基类的实例。虚继承通过在基类声明中使用 virtual 关键字来实现。

    # 二、虚继承的实现细节

    # 1. 基类声明

    在基类前加上 virtual 关键字,表示该基类使用虚继承。

    class Base {
    public:
        Base() {}
        void display() { std::cout << "Base class" << std::endl; }
    };
    
    1
    2
    3
    4
    5

    # 2. 虚继承的继承声明

    在派生类中继承虚基类时,也需要加上 virtual 关键字:

    class Derived1 : virtual public Base {
    public:
        Derived1() {}
    };
    
    class Derived2 : virtual public Base {
    public:
        Derived2() {}
    };
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    # 3. 最终派生类

    最终的派生类将从多个基类继承,并且只会有一个 Base 类的实例:

    class Final : public Derived1, public Derived2 {
    public:
        Final() {}
    };
    
    1
    2
    3
    4

    # 三、虚继承的内部实现

    虚继承在编译器和运行时通常通过引入一个虚表(vtable)和虚基类指针来实现。这些机制帮助管理虚继承的复杂性。

    # 1. 虚基类指针(Virtual Base Pointer)

    编译器为每个虚基类引入一个虚基类指针,用于指向虚基类在最终对象中的位置。每个派生类会维护一个指向虚基类的虚基类指针,用于正确地访问共享的虚基类实例。

    # 2. 虚表(Vtable)

    每个含有虚继承的类通常有一个虚表,用于存储指向虚基类的虚基类指针的地址。这个虚表在类的实例化时设置,并用于确保在继承结构中的正确访问。

    # 四、构造和析构过程

    虚继承的构造和析构过程比较复杂,因为虚基类的初始化和销毁需要特别处理:

    1. 构造过程:

      • 在构造最终派生类对象时,虚基类的构造函数会首先被调用。构造函数首先初始化所有虚基类,确保虚基类在派生类构造之前被正确初始化。
    2. 析构过程:

      • 在销毁最终派生类对象时,虚基类的析构函数会在派生类析构函数之后被调用。这样可以确保虚基类在所有派生类对象被销毁之后才被销毁。

    # 五、示例代码

    #include <iostream>
    
    class Base {
    public:
        Base() { std::cout << "Base constructor" << std::endl; }
        virtual ~Base() { std::cout << "Base destructor" << std::endl; }
        void display() { std::cout << "Base display" << std::endl; }
    };
    
    class Derived1 : virtual public Base {
    public:
        Derived1() { std::cout << "Derived1 constructor" << std::endl; }
        virtual ~Derived1() { std::cout << "Derived1 destructor" << std::endl; }
    };
    
    class Derived2 : virtual public Base {
    public:
        Derived2() { std::cout << "Derived2 constructor" << std::endl; }
        virtual ~Derived2() { std::cout << "Derived2 destructor" << std::endl; }
    };
    
    class Final : public Derived1, public Derived2 {
    public:
        Final() { std::cout << "Final constructor" << std::endl; }
        virtual ~Final() { std::cout << "Final destructor" << std::endl; }
    };
    
    int main() {
        Final obj;
        obj.display();
        return 0;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32

    输出:

    Base constructor
    Derived1 constructor
    Derived2 constructor
    Final constructor
    Final destructor
    Derived2 destructor
    Derived1 destructor
    Base destructor
    
    1
    2
    3
    4
    5
    6
    7
    8

    # 六、总结

    • 虚继承用于解决多重继承中的菱形继承问题,确保派生类只有一个基类的实例。
    • 虚基类指针和虚表是虚继承的实现机制,用于管理虚基类实例的共享。
    • 构造和析构过程对于虚继承较为复杂,因为需要正确地初始化和销毁虚基类。
    • 使用虚继承时,设计类层次结构时应注意虚继承带来的复杂性和额外开销。
    编辑 (opens new window)
    上次更新: 2024/09/13, 11:59:12
    多重继承
    对象池

    ← 多重继承 对象池→

    最近更新
    01
    ceph分布式存储-对象存储(RGW)搭建
    10-27
    02
    ceph分布式存储-集群客户端连接
    10-27
    03
    ceph分布式存储-管理crushmap
    10-27
    更多文章>
    Theme by Vdoing
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式