构造函数调用顺序与组合

Posted

技术标签:

【中文标题】构造函数调用顺序与组合【英文标题】:constructor calling order with composition 【发布时间】:2013-06-01 13:26:12 【问题描述】:

我有 A 类和 B 类。 C 类派生自 B 类,并具有 A 类对象作为组合。 http://ideone.com/JGT48M

#include "iostream"
using namespace std;

class A 
  int i;
public:
  A(int ii) : i(ii) 
      cout << "\n Constructor of A is called \n";

  
  ~A() 
      cout << "\n destructor  of A is called \n";
  
  void f() const 
;

class B 
  int i;
public:
  B(int ii) : i(ii) 
      cout << "\n Constructor of B is called \n";
  
  ~B() 
      cout << "\n destructor  of B is called \n";
  
  void f() const 
;

class C : public B 
  A a;
public:
  C(int ii) : a(ii), B(ii) 
      cout << "\n Constructor of C is called \n";
  
  ~C() 
  cout << "\n destructor  of C is called \n";
   // Calls ~A() and ~B()
  void f() const   // Redefinition
    a.f();
    B::f();
  
;

int main() 
  C c(47);
 ///:~

我已经读到构造函数的调用基于派生类构造函数中的调用方式。我的意思是让有一个名为 REF 的类从 REF_BASE1 和 REF_BASE2 派生然后

 REF (int ii) : REF_BASE2(ii), REF_BASE1 (ii) 

意味着首先调用 REF_BASE2 然后调用 REF_BASE1 再调用 REF 构造函数。 如果我们像

那样定义它
REF (int ii) : REF_BASE1(ii), REF_BASE2 (ii) 

这意味着将首先调用 REF_BASE1,然后调用 REF_BASE2,然后调用 REF 构造函数。

但是,在我上面的程序中,我明确地“错误地”声明了通过内部组合变量 A 先初始化然后 B 应该初始化,但是编译器以正确的方式进行操作,但没有让我知道我的错误

上述程序的输出与我在派生类constcutor初始化列表中指定的顺序无关

Constructor of B is called 

 Constructor of A is called 

 Constructor of C is called 

 destructor  of C is called 

 destructor  of A is called 

 destructor  of B is called 

我的问题是 1)为什么编译器不抱怨?还是我正确? 2) 派生构造函数中的顺序是否没有严格遵循?

【问题讨论】:

编译器向我抛出警告:"field 'a' will be initialized after base 'B' [-Wreorder]" 【参考方案1】:

我将从第二个问题开始:

2) 派生构造函数中的顺序是否没有严格遵循?

顺序不是出现在构造函数初始化列表中的顺序,而是基类出现在类定义中的顺序。

所以如果你的类定义是这样的:

struct A : B, C

    // ...
;

那么B的构造函数将在C的构造函数之前被调用,不管你在A的构造函数的初始化列表中指定的顺序。

C++11 标准的第 12.6.2/10 段规定:

在非委托构造函数中,初始化按以下顺序进行:

——首先,并且仅对于最派生类 (1.8) 的构造函数,虚拟基类在 它们出现在基类的有向无环图的深度优先从左到右遍历上的顺序, 其中“从左到右”是派生类 base-specifier-list 中基类的出现顺序。

——然后​​,直接基类按照它们出现在基说明符列表中的声明顺序进行初始化 (不管 mem-initializers 的顺序).

——然后​​,非静态数据成员按照它们在类定义中声明的顺序进行初始化 (同样不管内存初始化器的顺序)。

——最后,构造函数体的复合语句被执行。

现在第一个问题:

1) 为什么编译器不报错?

编译器可能会警告你构造函数初始化器列表中的初始化顺序与基类列表中的不同(GCC 对-Wall 进行处理),但并非必须如此。最终,只有后者很重要。

【讨论】:

以上是关于构造函数调用顺序与组合的主要内容,如果未能解决你的问题,请参考以下文章

继承和组合混搭的情况下,构造和析构函数的调用顺序

派生类的构造函数与析构函数的调用顺序

关于类继承的构造与析构调用分析

类的组合

C++中派生类的构造函数怎么显式调用基类构造函数?

实例构造函数与静态构造函数执行顺序