编译如何选择调用哪个构造函数?
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&)
显式调用)。
顺便说一句,你的复制构造函数应该是B(const B& b) : a(b.a) cout << "B(1)" << 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)
【讨论】:
以上是关于编译如何选择调用哪个构造函数?的主要内容,如果未能解决你的问题,请参考以下文章