包含其他对象的类的C ++隐式拷贝构造函数
我知道如果你没有实现自己,编译器有时会提供一个默认的拷贝构造函数。 我很困惑这个构造函数是做什么的。 如果我有一个包含其他对象的类,其中没有一个具有声明的拷贝构造函数,那么行为是什么? 例如,像这样的一个类:
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&)被调用。
这个副本构造它的基类,然后每个成员(recursion)
如果你像这样定义一个类:
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) {}
编译器提供了一个拷贝构造函数,除非你自己声明 (注意:不定义 )。 编译器生成的拷贝构造函数只是调用类(和每个基类)的每个成员的拷贝构造函数。
顺便说一下,赋值运算符和析构函数也是如此。 但是,对于默认的构造函数是不同的:只有在你自己没有声明任何其他构造函数时,才由编译器提供。
是的,编译器生成的拷贝构造函数按照成员在包含类中声明的顺序执行成员拷贝。 如果任何成员types本身不提供复制构造函数,则不能生成包含类的可能的复制构造函数。 如果你可以决定一些适当的方法来初始化不能被复制构造的成员的值,也许还可以用一个其他的构造函数来手工编写一个。
C ++ 默认拷贝构造函数创build一个浅拷贝。 浅拷贝不会创build原始对象可能引用的对象的新副本; 旧对象和新对象将简单地包含指向相同内存位置的不同指针。
编译器会为你生成所需的构造函数。
然而,只要你自己定义了一个复制构造函数,编译器就会放弃为这个类生成任何东西,如果你没有定义合适的构造函数,就会给出错误信息。
用你的例子:
class Baz { Baz(const Baz& b) {} int j; }; class Bar { int i; Baz baz; }; class Foo { Bar bar; };
尝试默认实例化或复制构造Foo将抛出一个错误,因为Baz不是可复制构造的,编译器不能为Foo生成默认和复制constroctor。