编译如何选择调用哪个构造函数?

Posted

技术标签:

【中文标题】编译如何选择调用哪个构造函数?【英文标题】:How does the compile choose which constructor to call? 【发布时间】:2019-07-08 13:53:50 【问题描述】:

这是我的代码。

当我删除第 11 行时,输出是

A(0)
B(0)
A(1)

关于最后一行“A(1)”,为什么要调用A类的第二个构造函数?

#include <iostream>

using namespace std;

class A 
public:
   A()  cout << "A(0)" << endl; 
   A(const A& a)  cout << "A(1)" << endl; 
;

class B 
public:
    B() : a()  cout << "B(0)" << endl; 
    // B(const B& b)  cout << "B(1)" << endl; 
private:
    A a;
;

int main() 
   B object1;
   B object2 = object1;
   return 0;

A(0)
B(0)
A(1)

【问题讨论】:

你为什么感到惊讶?自动生成的B 的复制构造函数调用A 的复制构造函数。当行处于活动状态时,B 的复制构造函数会调用A 的默认构造函数(因为A 的任何构造函数都不会从B::B(const B&amp;) 显式调用)。 顺便说一句,你的复制构造函数应该是B(const B&amp; b) : a(b.a) cout &lt;&lt; "B(1)" &lt;&lt; endl; 【参考方案1】:

什么时候

B(const B& b)  cout << "B(1)" << endl; 

被注释掉/删除,编译器会为您生成一个复制构造函数。这个提供的复制构造函数将复制该类的所有成员,因此在这种情况下,它将删除一个看起来像

的复制构造函数
B(const B& copy) : a(copy.a) 

这就是为什么您会看到 a 的复制构造函数被调用。

当你不注释/删除时

B(const B& b)  cout << "B(1)" << endl; 

您没有复制a,因为您没有告诉它这样做。相反,编译器所做的是通过将构造函数转换为

来为其创建默认初始化
B(const B& b) : a()  cout << "B(1)" << endl; 

所以调用默认构造函数而不是复制构造函数。

【讨论】:

【参考方案2】:

编译器正在为您生成一个copy constructor,它复制了成员a。为了复制成员a,它依次调用其复制构造函数,打印A(1)

【讨论】:

【参考方案3】:

因为object2 是使用 B 的隐式复制构造函数初始化的。隐式复制构造函数会隐式复制该类的所有数据成员,因此调用 A 的复制构造函数会打印“A(1)”。

【讨论】:

【参考方案4】:

您遇到的问题与认为注释掉第 11 行意味着您已删除该构造函数有关。

在 C++ 中,有几个构造函数会在您最终使用它们时自动生成,即使您自己没有声明它们也是如此。与 B 中被注释掉的构造函数具有相同签名的复制构造函数就是其中之一。

在您的情况下,您最终首先调用 B 的默认构造函数,它首先也使用默认构造函数构造它的成员 A。这应该给出您看到的输出,由于成员初始化排序,A 的复制构造函数的主体在 B 的主体之前到达。

然后,您使用赋值运算符创建一个 B 类型的新对象,该运算符隐式调用 B 现在生成的复制构造函数。这意味着 A 的复制构造函数也被调用,这是 B 的复制构造函数如何使用的规则自动生成。如果 A 的复制构造函数未注释,它会在打印输出中调用。

【讨论】:

【参考方案5】:

类 B 的编译器(带有注释的复制构造函数)隐式定义了调用类成员的复制构造函数的默认复制构造函数。

来自 C++ 20 标准(11.3.4.2 复制/移动构造函数)

14 非联合类的隐式定义的复制/移动构造函数 X 对其基和成员执行逐成员复制/移动...

类B的隐式定义的默认复制构造函数看起来像

B( const B &b ) : a( b.a )


这是一个演示程序

#include <iostream>

using namespace std;

class A 
public:
   A()  cout << "A(0)" << endl; 
   A(const A& a)  cout << "A(1)" << endl; 
;

class B 
public:
    B() : a()  cout << "B(0)" << endl; 
    // An analogy of the implicitly declared copy constructor
    B(const B& b) : a( b.a )
private:
    A a;
;

int main() 
   B object1;
   B object2 = object1;
   return 0;

程序输出将与删除编译器隐式生成的复制构造函数对应的B类的复制构造函数一样。

A(0)
B(0)
A(1)

【讨论】:

以上是关于编译如何选择调用哪个构造函数?的主要内容,如果未能解决你的问题,请参考以下文章

数组实例化调用构造函数? [复制]

对基本构造函数的条件调用

C++ - 继承,调用哪个基本构造函数? [复制]

如何避免在 C++ 中调用祖父构造函数?

构造函数的调用规则

为啥构造函数被调用两次