构造函数在私有继承类型之外可用吗?

时间:2016-07-31 13:45:40

标签: c++ constructor private-inheritance

标题只是我对以下示例感到困惑的几件事之一:

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

问题:

  1. Bmain的私有继承构造函数是如何出现的?这个问题类似于this帖子中的问题,但问题是关于在B内部使用该构造函数,这是不同的。
  2. 被调用的复制构造函数采用const A &参数。但是,如果我写B c(a)而不是B c(b),则代码不会编译。怎么会? (请注意,取消评论using中的B指令无效。
  3. 这是次要的,但仍然。为什么编译器不会警告我未使用的变量ac
  4. 问题2已被用于另一个post

2 个答案:

答案 0 :(得分:2)

  1. 您没有直接调用A的构造函数,B的默认构造函数正在为您执行此操作。如果这种方式有所不同,那么你永远无法构造任何私有地继承任何东西的类。

  2. 这是因为B没有类型A的复制构造函数。这里唯一可以应用的构造函数是B类型的(默认)复制构造函数,它将B作为参数。

  3. 构造函数绑定到它们的类,它们不像函数那样继承。如何解释......基本目标是确保每个类始终完全构建。因此,要构造任何类型的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),因此应用程序将中止。糟糕!

    这当然是一个人为的例子,但它表明使用非构造对象是一件非常糟糕的事情,因此该语言阻止你创建任何这样的对象。

    1. 他们没有被闲置。构造函数中有各种各样的活动(写到cout),不能简单地消除。
    2. 这种设计模式在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?

因为他们未使用。他们的构造做了一件事(创建了输出)。