前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Example】C++ 虚基类与虚继承 (菱形继承问题)

【Example】C++ 虚基类与虚继承 (菱形继承问题)

原创
作者头像
芯片烤电池
修改2022-04-28 10:07:12
9150
修改2022-04-28 10:07:12
举报
文章被收录于专栏:C++教程

C++ 是支持多继承的语言,但是实际项目开发中非必要请避免使用多继承以降低代码逻辑的复杂性。

当然 C++ 多继承的特性带来一些问题即菱形继承。

当一个类继承了两个来自同父类的子类后,会产生命名空间冲突及资源冗余。

【伪代码】

代码语言:javascript
复制
class Base{
public:
    int gem = 0; 
};

class Byte : public Base{};

class Expert : public Base{};

class Blu : public Byte, public Expert{
public:

    // Error 字段不明确
    void SetGem(int i) {
        this->gem = i;
    }

}

可以看到,产生错误的主要原因是,Byte 和 Expert 同样都继承了 Base,然后 Blu 又继承了 Byte 和 Expert 。

根据 C++ 类继承的机制,子类的大小=父类大小+子类自身成员大小。

因此,可以看出,实际上 Blu 类当中存在两个 Gem 成员变量,分别来自 Byte 和 Expert,使用 this 指针进行调用,会发生命名空间冲突错误,同时造成了资源的重复浪费

解决的方法也很简单,使用虚继承的方式:

【伪代码】

代码语言:javascript
复制
class Base{};

class Byte : virtual public Base{}; 

class Expert : virtual public Base{};

可以看到,在 Byte 和 Expert 的继承后面使用了 virtual 关键字进行了修饰。

这时,Base 便成了 Byte 和 Expert 的虚基类,达成了虚继承的方式,Base 类在最终的 Blu 类中只存在一个,所以不存在命名空间冲突及资源浪费。

虚基类并不是“绝对的”,而是“相对的”:虚基类在它自身声明、定义的时候无需任何修饰,只是在子类继承时进行 virtual 修饰。

然而这又牵扯到了另一种错误:

【伪代码】

代码语言:javascript
复制
class Base{};

class Byte : virtual public Base{}; 

class Expert : virtual public Base{};

class Frog : public Base{};

class Blu : public Byte, public Expert, public Frog{};

可以看到,Blu 继承了 Byte 、Expert、Frog 三个类,但是 Frog 类不是以虚继承的方式继承 Base 的。

所以在 Blu 类中仍然存在菱形继承的问题,所有需要将所有继承同一基类的上级父类继承方式声明为 virtual。

同时,在虚继承机制当中,虚基类是由最终的派生类进行初始化的,本身达成了一种 “间接继承” 的关系。

也就意味着最终的派生类在构造函数初始化中,要在初始化表中调用虚基类的构造函数进行初始化。

这样,就保证了虚基类不会被二次初始化。

最终正确代码是这样的:

代码语言:javascript
复制
class Base {
public:
    Base() {};
    ~Base() {};

public:
    int gem = 1;
};

class Byte : virtual public Base
{
public:
    Byte() {};
    ~Byte() {};

};

class Expert : virtual public Base
{
public:
    Expert() {};
    ~Expert() {};

};

class Blu : public Byte, public Expert
{
public:
    // 调用虚基类构造函数
    Blu() : Base(),Byte(),Expert() {};
    ~Blu() {};

    void SetGem(int i) {
        this->gem = i;
    }
};

附:C++ 类继承权限参考表

public 公有成员 :基类、派生类、友元、外部都可以访问

protected 保护成员: 基类、派生类、友元可以访问

private 私有成员 :基类、友元可以访问

继承方式

基类 public 成员

基类 protected 成员

基类 private 成员

public

public

protected

private

protected

protected

protected

private

privat

privat

private

private

另外,虚继承概念:【Example】C++ 虚基类与虚继承 (菱形继承问题)

虚继承时,子类的内存结构当中不包含父类。

====================================

芯片烤电池 C++ Example 2022-Spring Season Pass :

【Example】C++ 标准库常用容器全面概述

【Example】C++ 回调函数及 std::function 与 std::bind

【Example】C++ 运算符重载

【Example】C++ 标准库智能指针 unique_ptr 与 shared_ptr

【Example】C++ 接口(抽象类)概念讲解及例子演示

【Example】C++ 虚基类与虚继承 (菱形继承问题)

【Example】C++ Template (模板)概念讲解及编译避坑

【Example】C++ 标准库 std::thread 与 std::mutex

【Example】C++ 标准库多线程同步及数据共享 (std::future 与 std::promise)

【Example】C++ 标准库 std::condition_variable

【Example】C++ 用于编译时封装的 Pimpl 演示 (编译防火墙 Private-IMPL)

【Example】C++ 单例模式 演示代码 (被动模式、兼容VS2022编译)

====================================

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档