基础构造函数继承到派生类?

时间:2017-01-17 17:40:31

标签: c++ constructor

我一直认为基类构造函数/析构函数/朋友不是由派生类继承的。此链接证实了这一点: http://www.geeksforgeeks.org/g-fact-4/

我也知道我们可以在派生类初始化列表的初始化列表中编写基类构造函数。

话虽如此:我今天试图检查我的技能。但我没猜到这个程序的输出。

#include<iostream>

class A {
  int x, y;
  public:
    A(int a = 0, int b = 0) : x(a), y(b) {
      std::cout << "A ctor called" << std::endl;
    }
    void print_A() {
      std::cout << "x = " << x << std::endl;
      std::cout << "y = " << y << std::endl;
    }
};

class B : public A {
  int z;
  public:
    // I knew that A member can be initilized like this.
    B(int a = 0, int b = 0, int c = 0) : z(a), A(b, c) {
      std::cout << "C ctor called" << std::endl;
      // I was not aware about that. 
      A(b, c);
    }
    void print_B() {
      std::cout << "z = " << z << std::endl;
    }
};

int main() {
  B b(1, 2, 3);
  b.print_A();
  b.print_B();
}

输出:

A ctor called
C ctor called
A ctor called
x = 2
y = 3
z = 1

几个问题:

  • 如果构造函数/析构函数/朋友不是从base继承的,那么类'B'如何能够在这里访问类'A'的构造函数。

  • 你怎么得到这个输出?为什么两个“A”的构造者被称为。

4 个答案:

答案 0 :(得分:3)

你的理解是错误的。这样:

// I was not aware about that. 
  A(b, c);

没有初始化B的A成员,它(至少在概念上)在构造函数的主体中创建一个临时的,无名的局部变量,有点类似于你说过的话:

  A a(b, c);

A的构造函数是一个公共成员,所以任何东西都可以调用它。

答案 1 :(得分:1)

  

如果构造函数/析构函数/朋友不是从base继承的,那么类'B'如何才能访问类'A'的构造函数?

“未继承”并不意味着“派生类无法访问”。派生类当然可以引用基础构造函数。 B的构造函数执行了两次:

  • 第一次访问是在初始化列表中
  • 第二次访问是在B的构造函数的主体中;它会创建一个临时对象

继承构造函数意味着B的用户将能够访问B(int, int),他们无法访问 *

  

这似乎是一个构造函数调用。为什么要创建临时对象?

考虑这种方法:

void foo(const A& a);

一种常见的方法就是这样称呼它:

A a(1, 2);
foo(a);

但是C ++也允许你调用它而不在另一行创建A

foo(A(1, 2));

在这种情况下,C ++会创建一个临时对象,并将foo引用传递给它。当你写

A(1, 2)

C ++还可以通过调用其构造函数为您创建一个临时对象。

  

为什么两个'A'的构造函数被调用。

构造函数被调用两次;这就是你获得输出的原因。

* C ++ 11的using机制允许您实现效果very similar to constructor inheritance,前提是您遵循特定规则。

答案 2 :(得分:0)

每次调用A(b,c)时都会调用构造函数;

你在线上打电话

B(int a = 0, int b = 0, int c = 0) : z(a), A(b, c) {

你在b构造函数中调用它

// I was not aware about that. 
      A(b, c);

代码按预期工作

答案 3 :(得分:0)

  

如果构造函数/析构函数/朋友不是从base继承的,那么class&#39; B&#39;能够访问类&#39; A&#39;的构造函数。这里。

你的意思是在这些方面?

  // I was not aware about that. 
  A(b, c);

它不是访问构造函数&#34;从某种意义上说,你认为。它只是在构造函数体中创建(并立即丢弃)匿名临时A

  

你怎么得到这个输出?为什么有两个“A&#39;被称为。

因为您创建了A的两个实例:B b的基类子对象和匿名临时对象。

这是一个简单的实验来验证这一点,并进行以下记录:

// in A
A(int a = 0, int b = 0) : x(a), y(b) {
  std::cout << "A::A @" << static_cast<void*>(this) << std::endl;
}
~A() {
  std::cout << "A::~A @" << static_cast<void*>(this) << std::endl;
}

// in B
B(int a = 0, int b = 0, int c = 0) : z(a), A(b, c) {
  std::cout << "B::B @" << static_cast<void*>(this) << std::endl;
  A(b, c);
}
~B() {
    std::cout << "B::~B @" << static_cast<void*>(this) << std::endl;
}

我得到类似

的输出
A::A @0xffec7054
B::B @0xffec7054
A::A @0xffec704c
A::~A @0xffec704c
x = 2
y = 3
z = 1
B::~B @0xffec7054
A::~A @0xffec7054

看到第二个A::A的实例地址与第一个A::~A的实例地址不同(因此它是一个不同的对象),然后是auto subobject = static_cast<A*>(this); new (subobject) A(b, c); (因为匿名)临时超出范围)。

注意:

  1. 如果你可以&#34;调用构造函数&#34;正如你最初的建议,它看起来像

    A

    并且会非常错误。在b构造函数的主体启动之前,B : z(a), A(b, c)的{​​{1}}子对象在其自己的构造函数完成时完全构造。你不能在同一个空间重新创建一个新对象,旧对象会发生什么?

    这看似微不足道,但对于动态分配资源的对象,这将是一个严重的错误。这是不允许的。

  2. 您编写了初始化列表mask = df1.notnull() & df2.notnull() & df3.notnull() print (mask) a b c 0 True True False 1 True False True 2 True True True 3 False True True ,但应该知道在初始化派生类成员之前将构造基类子对象。也就是说,这两件事将以与你所写的相反的顺序发生。它不是(必然)错误,但值得了解。