c++ 如何在菱形问题中使(只)主路径为虚

wnavrhmk  于 2023-05-30  发布在  其他
关注(0)|答案(1)|浏览(95)

此代码无法编译:

#include <iostream>

class BaseV1 {
public:
    BaseV1(int i = 0) {
        std::cout << i;
    }
    void foo() {}
};

class DataV1 : public BaseV1 {
public:
    DataV1() : BaseV1(1) {}
};

class BaseV2 : public BaseV1 {};

class DataV2 : public DataV1, public BaseV2 {};

int main() {
    DataV2().foo(); // request for member ‘foo’ is ambiguous
    return 0;
}

所以我读到了钻石问题,我想出了我认为是这个解决方案,它使一个完整的路径(DataV2 : BaseV2 : BaseV1)虚拟。然而,这并没有改变任何事情;

#include <iostream>

class BaseV1 {
public:
    BaseV1(int i = 0) {
        std::cout << i;
    }
    void foo() {}
};

class DataV1 : public BaseV1 {
public:
    DataV1() : BaseV1(1) {}
};

class BaseV2 : virtual public BaseV1 {};

class DataV2 : public DataV1, virtual public BaseV2 {};

int main() {
    DataV2().foo(); // request for member ‘foo’ is ambiguous
    return 0;
}

我通过使另一个路径(DataV2 : DataV1 : BaseV1)虚拟化来编译示例,这不是我真正想要的,因为我不喜欢过多地接触现有的代码库(V1):

#include <iostream>

class BaseV1 {
public:
    BaseV1(int i = 0) {
        std::cout << i;
    }
    void foo() {}
};

class DataV1 : virtual public BaseV1 { // I do not want to make this virtual, I think.
public:
    DataV1() : BaseV1(1) {}
};

class BaseV2 : virtual public BaseV1 {};

class DataV2 : virtual public DataV1, public BaseV2 {
public:
    DataV2() : DataV1() {}
};

int main() {
    DataV1().foo(); // this prints 1, as expected
    DataV2().foo(); // this prints 0, not 1 - so DataV1's constructor is skipped
    return 0;
}

即使代码能够编译,它在运行时也会失败,因为似乎跳过了DataV1的构造函数。
所以我的问题是如何避免这里的菱形问题,理想情况下不涉及V1继承路径?

5gfr0r5j

5gfr0r5j1#

virtual并不能神奇地解决所有问题。BaseV1virtual继承使得派生最多的类直接从BaseV1继承。但这意味着DataV2仍然有两个BaseV1类-一个直接继承自BaseV1,另一个在DataV1内部。
可能的解决方案:

  • 使DataV1也具有虚拟继承(显然)
  • DataV2不继承DataV1
  • 保持一切原样,并为DataV2提供自己的void foo()方法,该方法委托给特定的内部实现(例如:void foo() {DataV1::foo();}

相关问题