对于第二个问题,可能是因为你没有让它被隐式定义。如果构造函数只是隐式声明,则没有错误。例子:
struct A { A(int); };
struct B : A { };
// goes fine up to here
// not anymore: default constructor now is implicitly defined
// (because it's used)
B b;
对于您的第一个问题 - 这取决于编译器使用的名称。我不知道标准规定了什么,但是这个代码是正确的,因为外部类名(而不是继承的类名)是可访问的:
class A {};
class B: private virtual A {};
class C: public B { C(): ::A() { } }; // don't use B::A
也许此时标准未指定。我们得看看。
代码似乎没有任何问题。此外,有迹象表明该代码是有效的。(虚拟)基类子对象是默认初始化的 - 没有文本暗示类名的名称查找在C
. 这是标准所说的:
12.6.2/8
(C++0x)
如果给定的非静态数据成员或基类不是由 mem-initializer-id 命名(包括由于构造函数没有 ctor-initializer 而没有 mem-initializer-list 的情况)并且实体不是虚拟的抽象类的基类
[...] 否则,实体是默认初始化的
并且 C++03 有类似的文本(你不太清楚的文本 - 它只是说它的默认构造函数在一个地方被调用,而在另一个地方它依赖于类是否是 POD)。对于默认初始化子对象的编译器,它只需要调用它的默认构造函数——不需要首先查找基类的名称(它已经知道考虑什么基类)。
考虑一下这段代码,它肯定是有效的,但如果这样做会失败(参见12.6.2/4
C++0x 中的代码)
struct A { };
struct B : virtual A { };
struct C : B, A { };
C c;
A
如果编译器的默认构造函数只是简单地在 中查找类名C
,那么关于要初始化的子对象的查找结果就会不明确,因为同时找到了非虚拟A
和虚拟A
的类名。如果您的代码旨在格式错误,我会说标准当然需要澄清。
对于构造函数,请注意12.4/6
关于析构函数的内容C
:
所有的析构函数都被调用,就好像它们被一个限定名引用一样,也就是说,忽略更多派生类中任何可能的虚拟覆盖析构函数。
这可以用两种方式解释:
在我看来,标准在这里不太清楚。第二种方法将使其有效(通过3.4.3/6
C++0x,因为两个类名A
都在全局范围内查找),而第一种方法将使其无效(因为两者A
都会找到继承的类名)。它还取决于搜索从哪个子对象开始(我相信我们将不得不使用虚拟基类的子对象作为起点)。如果这像
virtual_base -> A::~A();
然后我们将直接找到虚拟基类的名称作为公共名称,因为我们不必经过派生类的范围并找到名称为不可访问。同样,推理是相似的。考虑:
struct A { };
struct B : A { };
struct C : B, A {
} c;
如果析构函数只是调用this->A::~A()
,则此调用将无效,因为A
作为继承的类名的查找结果不明确(您不能从作用域引用直接基类对象的任何非静态成员函数C
,请参阅10.1/3
C ++03)。它必须唯一地标识所涉及的类名,并且必须以类的子对象引用开头,例如a_subobject->::A::~A();
.