通过将指向"this"的指针传递给基构造函数来消除C++菱形继承
Eliminating C++ diamond inheritance by passing a pointer to "this" to base constructor
我了解C++如何通过使用虚拟继承来解决多重继承中的钻石问题。假设以下情况:
class A {
int num;
public:
int get_num() const { return num; }
};
class B : public A {
void foob() { int x = get_num(); }
};
class C : public A {
void fooc() { int x = get_num(); }
};
class D : public B, public C {
void food() { int x = get_num(); }
};
get_num()
调用在food()
内部不明确。我知道我可以通过调用A::get_num()
或通过使用virtual public A
的虚拟继承来修复它。但我可以看到第三种方法:
class A {
int num;
public:
int get_num() const { return num; }
};
class B : public A {
void foob() { int x = get_num(); }
};
class C { // won't inherit from A anymore
const A& base; // instead keeps a reference to A
void fooc() { int x = base.get_num(); }
public:
explicit C(const A* b) : base(*b) { } // receive reference to A
};
class D : public B, public C {
void food() { int x = get_num(); }
public:
D() : C(this) { } // pass "this" pointer
};
外部代码不需要将C视为A.
考虑到它对我特定的类层次结构设计没有影响,与虚拟继承方式相比,第三种方法有什么优势吗?或者,就成本而言,它最终是一样的东西?
祝贺您!您刚刚重新发明了组合而非继承的原理!
如果这适用于您的设计,则意味着C
实际上不是A
的一种,并且首先没有真正的理由使用继承。
但不要忘记5的规则!虽然你的方法原则上应该有效,但这里有一个严重的错误:在你当前的代码中,如果你复制一个D
对象,它的克隆使用了对基的错误引用(它没有引用它自己的基,这可能会导致非常严重的错误…
隐藏问题的演示
让我们让A::get_num()
更冗长一点,这样它就可以告诉我们调用它的对象的地址:
int get_num() const {
cout << "get_num for " << (void*)this <<endl;
return num;
}
为了演示的目的,让我们在C
中添加一个成员函数:
void show_oops() { fooc(); }
与D
:相同
void show() { food(); }
现在,我们可以通过运行以下小片段来试验这个问题:
int main() {
D d;
cout<<"d is "<<(void*)&d<<endl;
d.show();
d.show_oops();
D d2=d;
cout<<"d2 is "<<(void*)&d2<<endl;
d2.show();
d2.show_oops();
}
这是一个在线演示。您会注意到d2
确实会产生不一致的结果,如下图所示:
d is 0x7fffe0fd11a0
get_num for 0x7fffe0fd11a0
get_num for 0x7fffe0fd11a0
d2 is 0x7fffe0fd11b0
get_num for 0x7fffe0fd11b0
get_num for 0x7fffe0fd11a0 <<< OUCH !! refers to the A element in d !!
你不仅引用了错误的对象,而且如果d对象会消失,你会有一个悬空的引用,所以UB。
相关文章:
- "error: no matching function for call to"构造函数错误
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 为什么在C++中使用私有复制构造函数与删除复制构造函数
- 选择要调用的构造函数
- 如何委托派生类使用其父构造函数?
- 构造函数正在调用一个使用当前类类型的函数
- 没有用于初始化C++中的变量模板的匹配构造函数
- 初始化具有非默认构造函数的std::数组项的更好方法
- 当从函数参数中的临时值调用复制构造函数时
- 在c++构造函数中使用随机字符串生成器
- 一对向量构造函数:初始值设定项列表与显式构造
- 从构造函数抛出异常时如何克服内存泄漏
- 我不明白为什么我声明一个空的内部结构并将其传递给构造函数
- 继承:构造函数,初始化C++11中基类的类C数组成员
- 具有默认模板类型的默认构造函数的类型推导
- 使用dynamic_cast和构造函数时出错
- 在c++中使用向量时,如何调用构造函数和析构函数
- 奇怪的构造函数行为