class Foo {
Foo(); // 默认构造函数
Foo(const Foo); // 拷贝构造函数
Foo& operator=(const Foo&); // 赋值运算符
};
拷贝构造函数
如果没有自己定义,编译器会自行定义
拷贝初始化
string dots(10, ','); // 直接初始化
string s(dots); // 直接初始化,调用拷贝构造函数
string s2 = dots; // 拷贝初始化
string null_book = "9-999-99999-9"; // 拷贝初始化
string nines = string(100, '9'); // 拷贝初始化
拷贝初始化的时机
- 用=定义变量时
- 将对象作为实参传递给一个非引用类型的形参
- 该特性解释了为什么拷贝构造函数自己的参数必须是引用类型。如果其参数不是引用类型,则调用永远也不会成功–为了调用拷贝构造函数,我们必须拷贝它的实参,但为了拷贝,又需要调用拷贝构造函数,如此循环。
- 从一个返回类型为非引用类型的函数返回一个队形
- 用花括号列表初始化一个数组中的元素或一个聚合类中的成员
补充:
- 直接初始化: 使用普通的函数匹配即可完成初始化。也就是说只是直接调用类的构造函数或拷贝构造函数就能完成初始化的就是直接初始化,s调用了拷贝构造函数
- 拷贝初始化: 将对象拷贝到正在创建的对象当中,如果需要还要进行类型转换。这里也就是间接调用拷贝构造函数,当然大部分情况调用拷贝构造函数,有时也可能调用移动构造函数。
拷贝赋值运算符
如果自己没有定义,编译器会自行生成合成拷贝赋值运算符(synthesized copy-assignment operator)
Sales_data trans, accum;
trans = accum; // 使用Sales_data的拷贝赋值运算符
重载赋值运算符
本质上是函数,其名字由operator关键字后接表示要定义的运算符的符号组成。因此,赋值运算符就是一个名为operator=的函数。
重载运算符的参数表示运算符的运算对象。某些运算符,包括赋值运算符,必须定义为成员函数。如果一个运算符是一个成员函数,其左侧运算对象就绑定到隐式的this参数。对于一个二元运算符,例如赋值运算符,其右侧运算对象作为显式参数传递。
为了与内置类型的赋值保持一致,赋值运算符通常返回一个指向其左侧运算对象的引用。
移动构造函数
移动赋值运算符
析构函数
释放对象使用的资源,并销毁对象的非static数据成员。无参数,无返回值,因此也不能被重载。
析构函数工作
构造函数中,成员的初始化是在函数体执行之前完成的,且按照它们在类中出现的顺序。
析构函数中,首先执行函数体,然后销毁成员。成员按初始化顺序的逆序销毁。
什么时候调用析构函数
任何对象被销毁的时候都会自动调用其析构函数
- 变量在离开其作用域时被销毁
- 当对象被销毁时,其成员被销毁
- 容器被销毁时,其元素被销毁
- 对于动态分配的对象,当对指向它的指针应用delete运算符时被销毁
- 对于临时对象,当创建它的完整表达式结束时被销毁
析构函数体自身并不直接销毁成员。成员是在析构函数体之后隐含的析构阶段中被销毁的。在整个对象销毁过程中,析构函数是作为成员销毁步骤之外的另一部分而进行的。