标题只是我对以下示例感到困惑的几件事之一:
struct A {
A() {
std::cout << "Default constructor of A" << std::endl;
}
A(const A &) {
std::cout << "Copy constructor of A" << std::endl;
}
};
struct B : private A {
// using A::A; // does not help for question 2.
};
int main() {
A a;
B b;
B c(b); // Does not work with `a` as an argument
return 0;
}
此示例输出:
Default constructor of A
Default constructor of A
Copy constructor of A
问题:
答案 0 :(得分:2)
您没有直接调用A的构造函数,B的默认构造函数正在为您执行此操作。如果这种方式有所不同,那么你永远无法构造任何私有地继承任何东西的类。
这是因为B没有类型A的复制构造函数。这里唯一可以应用的构造函数是B类型的(默认)复制构造函数,它将B作为参数。
构造函数绑定到它们的类,它们不像函数那样继承。如何解释......基本目标是确保每个类始终完全构建。因此,要构造任何类型的A(无论是独立的,还是作为B的一部分),A的构造函数必须运行。类似地,要构造B类的对象,B的构造函数必须运行。如果你继承了#39;在A :: A()构造函数中,您将能够构造未完全构造的B&#。在这种情况下,A :: A()构造函数没有运行B的构造序列的任何部分,使B处于无效状态。
让我们尝试一些不同的来源。我们按照原样保留A,然后改变B:
struct B : private A {
B () { val = 42; }
void foo () { if (val != 42) abort (); }
using A::A;
int val;
};
现在让我们说,呃,在没有构建它的情况下获得B:
B b (a); // illegal, but for the sake of argument.
b.foo ();
我们已经指定使用A :: A构造函数构造此B,因此唯一要执行的代码是A :: A()。特别是,B :: B()不会被执行,因此val将具有当时堆栈上存在的任何值。它是42的概率是1 ^ 2 ^ 32,换句话说,不太可能。
调用B.foo()时会发生什么?对象未处于有效状态(val不是42),因此应用程序将中止。糟糕!
这当然是一个人为的例子,但它表明使用非构造对象是一件非常糟糕的事情,因此该语言阻止你创建任何这样的对象。
这种设计模式在C ++中多次出现,例如在std :: mutex中。只是声明一个锁就足以进行锁定和解锁,但是在声明之后不需要引用锁。由于这是一种常见的设计模式,警告是不合适的。
只有编译器能够证明构造和破坏局部变量没有副作用,并且发现它没有被使用时,您才会看到警告(取决于编译器)。
答案 1 :(得分:1)
为什么B的私人继承的构造函数在main中可用?
他们不是。
然而,B
本身可以使用它们,因为它们已被继承,并且它是B调用继承的基础构造函数。
显然,如果不是这样,那么私有继承将完全没用,因为你永远无法实例化一个私有继承类。
他们被调用,输出就是
的证明
不,它不是。您可以妄下结论。有中间函数调用(B
中隐式定义的构造函数),它们不会产生您未考虑的输出。
被调用的复制构造函数采用const A&amp;论点。但是,如果我写B c(a)而不是B c(b),则代码不会编译。怎么样?
不,它没有。您的代码行调用B
中带有const B&
参数的复制构造函数。 B
没有一个带有const A&
参数的复制构造函数。
该复制构造函数依次在内部调用base的复制构造函数,生成您看到的输出。
构造函数不会以您似乎相信的方式继承。
这是次要的,但仍然如此。为什么编译器不会警告我有关未使用的变量a和c?
因为他们不未使用。他们的构造做了一件事(创建了输出)。