You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++菱形继承初始化问题:默认构造函数被忽略?

C++菱形继承中的虚拟基类初始化问题

可能重复的问题

  • Diamond Problem C++: Derived class of diamond calls default constructor

问题示例代码

#include <iostream>
#include <cassert>

struct MyEnum {
    enum valid { CASE1, CASE2, DEFAULT };
    valid value;
    MyEnum(valid value = DEFAULT) : value(value) {}
};

class Base {
protected:
    MyEnum value;

public:
    Base() = default;
    Base(MyEnum value) : value(value) {
        std::cout << "Base::Base(MyEnum).\n";
    }  
};

class ReadableBase : public virtual Base {
public:
    ReadableBase() = default;
    ReadableBase(MyEnum value) : Base(value) {
        std::cout << "ReadableBase::ReadableBase(MyEnum).\n";
    }

public:
    MyEnum read() { return value; }
};

class WriteableBase : public virtual Base {
public: 
    WriteableBase() = default;
    WriteableBase(MyEnum value) : Base(value) {
        std::cout << "WriteableBase::WriteableBase(MyEnum).\n";
    }

public:
    void write(MyEnum value) { this->value = value; }
};

class ReadWriteBase : public ReadableBase, 
                      public WriteableBase {
public:
    ReadWriteBase() = default;
    ReadWriteBase(MyEnum value) : ReadableBase(value), WriteableBase(value) {}
};

int main(int, char*[]) {
    // 错误初始化为value = MyEnum::valid::DEFAULT
    ReadWriteBase rw(MyEnum::valid::CASE1);
    
    // 此时操作正常
    rw.write(MyEnum::valid::CASE2);
}

问题现象

尽管ReadWriteBase传入了MyEnum::valid::CASE1,但程序中Base::value以及其派生类中的对应值都是MyEnum::valid::DEFAULT,且Base::Base(MyEnum)构造函数并未被调用:

assert(rw.ReadableBase::value  == MyEnum::valid::DEFAULT); // true
assert(rw.WriteableBase::value == MyEnum::valid::DEFAULT); // true
assert(rw.Base::value          == MyEnum::valid::DEFAULT); // true

核心问题

  1. 该现象的原因是什么?如何正确实现初始化?
  2. 是否有更优的实现方案?

原因分析

在C++的虚拟继承机制中,虚拟基类的初始化由最底层的派生类(即ReadWriteBase)负责,而非中间的派生类(ReadableBase、WriteableBase)。

当创建ReadWriteBase对象时,即使ReadableBase和WriteableBase的构造函数中写了Base(value),这些调用会被编译器忽略——因为虚拟基类的初始化权限被移交给了最底层的派生类。此时ReadWriteBase的构造函数没有显式调用Base的带参构造,所以编译器会自动调用Base的默认构造函数,导致value被初始化为DEFAULT。

正确的初始化实现

修改ReadWriteBase的构造函数,显式调用虚拟基类Base的带参构造:

class ReadWriteBase : public ReadableBase, 
                      public WriteableBase {
public:
    ReadWriteBase() = default;
    // 显式初始化虚拟基类Base
    ReadWriteBase(MyEnum value) : Base(value), ReadableBase(value), WriteableBase(value) {}
};

这样创建ReadWriteBase对象时,会先调用Base::Base(MyEnum)完成初始化,再依次调用ReadableBase和WriteableBase的构造函数,最终value会被正确设置为传入的CASE1。

更优的实现方案

如果场景允许,可以考虑以下几种更简洁的设计:

1. 组合替代继承

避免菱形继承的复杂性,采用组合的方式封装读写功能:

class ReadWrite {
private:
    MyEnum value;
public:
    ReadWrite(MyEnum val = MyEnum::DEFAULT) : value(val) {}
    MyEnum read() const { return value; }
    void write(MyEnum val) { value = val; }
};

这种方式没有继承层级的问题,代码更直观,也符合"优先使用组合而非继承"的设计原则。

2. 抽象接口+实现类

定义纯虚的读写接口,再由具体类实现:

class Readable {
public:
    virtual MyEnum read() const = 0;
    virtual ~Readable() = default;
};

class Writeable {
public:
    virtual void write(MyEnum val) = 0;
    virtual ~Writeable() = default;
};

class ReadWriteImpl : public Readable, public Writeable {
private:
    MyEnum value;
public:
    ReadWriteImpl(MyEnum val = MyEnum::DEFAULT) : value(val) {}
    MyEnum read() const override { return value; }
    void write(MyEnum val) override { value = val; }
};

这种方式通过接口分离关注点,同时避免了虚拟继承的初始化问题,扩展性更好。


内容的提问来源于stack exchange,提问作者buzzysin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 18:47:01