包含其他对象的类的 C++ 隐式复制构造函数
-
05-07-2019 - |
题
我知道如果您不自己实现,编译器有时会提供默认的复制构造函数。我对这个构造函数到底做了什么感到困惑。如果我有一个包含其他对象的类,但这些对象都没有声明的复制构造函数,那么行为会是什么?例如,像这样的类:
class Foo {
Bar bar;
};
class Bar {
int i;
Baz baz;
};
class Baz {
int j;
};
现在如果我这样做:
Foo f1;
Foo f2(f1);
默认的复制构造函数会做什么?编译器生成的复制构造函数会在 Foo
调用编译器生成的构造函数 Bar
复制过来 bar
, ,然后它将调用编译器生成的复制构造函数 Baz
?
解决方案
Foo f1;
Foo f2(f1);
是的,这将达到您的期望:
调用 f2 复制构造函数 Foo::Foo(Foo const&) 。
该副本构造其基类,然后构造每个成员(递归地)
如果你定义一个类是这样的:
class X: public Y
{
private:
int m_a;
char* m_b;
Z m_c;
};
以下方法将由您的编译器定义。
- 构造函数(默认)(2 个版本)
- 构造函数(副本)
- 析构函数(默认)
- 赋值运算符
构造函数:默认:
实际上有两个默认构造函数。
一个用于 zero-initialization
而另一个用于 value-initialization
. 。使用的取决于你是否使用 ()
是否在初始化期间。
// Zero-Initialization compiler generated constructor
X::X()
:Y() // Calls the base constructor
// If this is compiler generated use
// the `Zero-Initialization version'
,m_a(0) // Default construction of basic PODS zeros them
,m_b(0) //
m_c() // Calls the default constructor of Z
// If this is compiler generated use
// the `Zero-Initialization version'
{
}
// Value-Initialization compiler generated constructor
X::X()
:Y() // Calls the base constructor
// If this is compiler generated use
// the `Value-Initialization version'
//,m_a() // Default construction of basic PODS does nothing
//,m_b() // The values are un-initialized.
m_c() // Calls the default constructor of Z
// If this is compiler generated use
// the `Value-Initialization version'
{
}
笔记:如果基类或任何成员没有有效的可见默认构造函数,则无法生成默认构造函数。除非您的代码尝试使用默认构造函数,否则这不是错误(那么只是编译时错误)。
构造函数(副本)
X::X(X const& copy)
:Y(copy) // Calls the base copy constructor
,m_a(copy.m_a) // Calls each members copy constructor
,m_b(copy.m_b)
,m_c(copy.m_c)
{}
笔记:如果基类或任何成员没有有效的可见复制构造函数,则无法生成复制构造函数。这不是一个错误,除非您的代码尝试使用复制构造函数(那么只是一个编译时错误)。
赋值运算符
X& operator=(X const& copy)
{
Y::operator=(copy); // Calls the base assignment operator
m_a = copy.m_a; // Calls each members assignment operator
m_b = copy.m_b;
m_c = copy.m_c;
return *this;
}
笔记:如果基类或任何成员没有有效的可行赋值运算符,则无法生成赋值运算符。这不是一个错误,除非您的代码尝试使用赋值运算符(那么只是一个编译时错误)。
析构函数
X::~X()
{
// First runs the destructor code
}
// This is psudo code.
// But the equiv of this code happens in every destructor
m_c.~Z(); // Calls the destructor for each member
// m_b // PODs and pointers destructors do nothing
// m_a
~Y(); // Call the base class destructor
- 如果 任何 声明了构造函数(包括副本),则编译器不会实现默认构造函数。
- 如果声明了复制构造函数,则编译器将不会生成复制构造函数。
- 如果声明了赋值运算符,则编译器将不会生成赋值运算符。
- 如果声明了析构函数,编译器将不会生成析构函数。
查看您的代码,会生成以下复制构造函数:
Foo::Foo(Foo const& copy)
:bar(copy.bar)
{}
Bar::Bar(Bar const& copy)
:i(copy.i)
,baz(copy.baz)
{}
Baz::Baz(Baz const& copy)
:j(copy.j)
{}
其他提示
编译器提供了一个复制构造函数,除非你自己声明(注意:不是 define )。编译器生成的复制构造函数只是调用类的每个成员(以及每个基类)的复制构造函数。
赋值运算符和析构函数BTW也是如此。但是默认构造函数不同:只有在您不自己声明任何其他构造函数时,才由编译器提供。
是的,编译器生成的复制构造函数按成员在包含类中声明的顺序执行成员复制。如果任何成员类型本身不提供复制构造函数,则无法生成包含类的可能复制构造函数。如果你可以决定使用一些适当的方法初始化不能复制构造的成员的值 - 也许可以通过使用其他构造函数之一,那么仍然可以手动编写一个。
编译器将为您生成所需的构造函数。
但是,只要您自己定义一个复制构造函数,编译器就会放弃为该类生成任何内容,如果您没有定义适当的构造函数,它将给出错误。
使用您的示例:
class Baz {
Baz(const Baz& b) {}
int j;
};
class Bar {
int i;
Baz baz;
};
class Foo {
Bar bar;
};
尝试默认实例化或复制构造Foo会抛出错误,因为Baz不是可复制构造的,并且编译器无法为Foo生成默认和复制constroctor。