以下代码说明了我的问题:
struct Base {
Base(int n) : n(n) {}
virtual ~Base() = 0;
int n;
};
Base::~Base() {}
struct A : public virtual Base {
A(int n) : Base(n) {}
virtual ~A() = 0;
};
A::~A() {}
struct B : public virtual Base {
B(int n) : Base(n) {}
virtual ~B() = 0;
};
B::~B() {}
struct Test : public virtual A, public virtual B {
Test(int n) : Base(n), A(n), B(n) {} // how to avoid this duplication?
};
int main() {
Test c(0);
(void)c;
}
如您所见,Test
构造函数必须明确初始化Base
,A
和B
。这是正常的吗?或者有办法避免冗余吗?
答案 0 :(得分:4)
#include <assert.h>
struct Base {
Base(int n) : n(n) {}
virtual ~Base() = 0;
int n;
protected:
Base() { assert( false ); }
};
Base::~Base() {}
struct A : public virtual Base {
virtual ~A() = 0;
};
A::~A() {}
struct B : public virtual Base {
virtual ~B() = 0;
};
B::~B() {}
struct Test : public virtual A, public virtual B {
Test(int n) : Base(n) {} // how to avoid this duplication?
};
int main() {
Test c(0);
(void)c;
}
答案 1 :(得分:2)
没有冗余初始化。虚拟基类的构造只由最派生类的构造函数调用一次。
更新这个问题可以解释为两件事,(1)如何避免在运行时对构造函数进行冗余调用,以及(2)如何避免编写冗余初始化列表。显然,作者的意思是(2)。我在回答(1)。
答案 2 :(得分:2)
首先,您的方案中Test
和A
几乎不需要B
,因为A
和B
似乎都不是用作基础。
并且,是的,Base
必须在最派生的类 中初始化。原因很简单,test
的直接基类共享相同的Base
子对象 。为了做到这一点,它必须在构造它们中的任何一个之前由最派生的类构造
就个人而言,我一直认为A
或B
也可以构造它,它由声明顺序确定为基类,并拧紧另一个的构造函数。但是当调用不同的构造函数时,这将启用非常微妙的错误,并且基类声明顺序的微妙问题可能会引入令人惊讶的行为更改。 (并不是说我们不会在语言的其他地方出现这样的问题,但少一点可能是件好事。)
但是,请注意,虽然C ++为您提供了所有可能获得的自由,但通常最好是虚拟基类是 抽象类 , < em>没有会员数据 ,因此 只是默认构造函数 。由于这将被隐式调用,因此任何派生类都不必为显式调用构造函数而烦恼。