一、概述
当我们定义一个类时,我们显式或隐式地指定在此类型进行对象拷贝、移动、赋值和销毁操作
一个类通过定义五种特殊的成员函数来控制这些操作,包括:拷贝构造函数、拷贝赋值运算符、移动构造函数、移动赋值运算符、析构函数,这些操作称为拷贝控制函数
二、拷贝构造函数
1、如果一个构造函数的第一个参数是自身类类型的引用,且任何额外参数都有默认值,则此构造函数是拷贝构造函数
class Node
{
public:
int num;
string name;
public:
Node(const Node& node) :num(node.num), name(node.name) {}
};
2、如果我们没有为一个类定义拷贝构造函数,编译器会自动生成,与合成默认构造函数不同,即使我们定义了其他构造函数,也会自动合成构造函数
3、在合成拷贝构造函数中,编译器从给定对象中依次将每个非static成员拷贝到正在创建的对象中
4、拷贝初始化:
当我们使用直接初始化时,是调用函数匹配功能来选择最合适的参数的构造函数;当我们使用拷贝初始化时,是要求编译器将右侧运算对象拷贝到正在创建的对象中
string s1(s); //直接初始化
string s2 = s; //拷贝初始化
拷贝初始化的使用场景:
(1)、用=定义变量;
(2)、将一个对象作为实参传递给非引用类型的形参;
(3)、从一个返回类型为非引用的函数返回一个对象;
(4)、花括号列表初始化一个数组或聚合类的成员
5、从拷贝初始化可知:拷贝构造函数的参数必须是引用类型。否则,如果参数不是引用类型,为了调用拷贝构造函数,它必须将实参拷贝到形参,但拷贝的过程又需要调用拷贝构造函数
三、拷贝赋值运算符
1、与拷贝构造函数一样,如果类未定义自己的拷贝赋值运算符,编译器将会自己合成
2、拷贝赋值运算符实际上就是重载赋值运算符
class Node
{
public:
int num;
string name;
public:
Node& operator= (const Node& node)
{
num = node.num;
name = node.name;
return *this;
}
};
四、析构函数
1、当一个类没有定义自己的析构函数时,编译器会为它定义一个合成析构函数
2、析构函数用于释放对象所使用的资源,并销毁对象的非static数据成员
3、析构函数的函数名由波浪号接类名构成,不接受参数,因此不能被重载,故析构函数只有一个
class Node
{
public:
int num;
string name;
public:
~Node() {}
};
4、内置类型(int等)没有析构函数,因此在销毁内置类型成员什么也不需要做
5、析构函数的析构部分是隐式的,如果隐式销毁一个内置指针类型成员,将不会delete它所指向的对象
6、何时调用析构函数:
(1)、变量离开作用域时被销毁;
(2)、当一个对象被销毁时,其成员被销毁;
(3)、容器(标准库或数组)被销毁时,其元素被销毁;
(4)、对于动态分配的对象,当对它指向的指针应用delete运算符时才会被销毁
(5)、临时对象(如返回值为对象),当创建它的完整表达式结束时被销毁
7、合成析构函数不会delete一个指针数据成员,此时需要定义一个析构函数来释放构造函数分配的内存
五、阻止拷贝
1、对于某些类来说,拷贝构造函数和拷贝赋值运算符对类来说没有意义,甚至有时必须阻止,例如iostream类中组织了拷贝,避免多个对象写入或读取相同的IO缓冲
2、为了阻止拷贝,我们可以将其定义为删除的函数,在函数的参数列表后面加上=delete来指出它被定义为删除的
class Node
{
public:
int num;
string name;
public:
Node() = default;
Node(const Node&) = delete;
Node& operator=(const Node&) = delete;
~Node() = default;
};
3、析构函数一般不能使删除的函数,否则将无法定义该类型的变量或者创建该类的临时变量
对于删除了析构函数的类型,我们虽然不能定义这种类型的变量或者成员,但是可以动态分配这类型的对象,但是不能释放这些对象(不可delete)
4、在某些情况下,合成的拷贝控制成员可能是删除的,本质上,如果类有成员不能默认拷贝、构造、复制或销毁,则对应成员函数将被定义为删除的:
(1)、如果类的某个成员的析构函数是删除的或不可访问的,则类的合成析构函数定义为删除的
(2)、如果类的某个成员的析构函数或拷贝构造函数是删除的或不可访问的,则类的合成拷贝构造函数定义为删除的
(3)、如果类的某个成员的拷贝构造运算符是删除的或不可访问的,或者类有一个const或引用成员,则类的合成拷贝赋值运算符被定义为删除的
(4)、如果类的某个成员的析构函数是删除的或不可访问的,或者类有一个引用成员且没有类内初始化器,或者类有一个const成员且没有类内初始化器且其类型未显式定义默认构造函数,则该类的默认构造函数被定义为删除的