1.拷贝控制操作
- 拷贝构造函数
- 移动构造函数 //用同类型的对象初始化本对象
- 拷贝赋值运算符
- 移动赋值运算符 //将一个对象赋予同类型的另一个对象
- 析构函数 //销毁对象
注意点:
①如果一个类没有定义所有这些拷贝控制成员,编译器会自动为它定义缺失的操作。
②声明后加=default来显示要求编译器生成合成的版本,只能对具有合成版本的成员函数使用(默认构造函数&拷贝控制成员)。
③声明后加**=delete来定义删除的函数,来阻止该函数的调用,可以对任何函数使用,与=default不同。
④< utility >头文件中的move函数调用均采用std::move**
⑤swap(a,b); //若a和b均为内置类型,此时会调用标准库std::swap,否则按照匹配程度最优的调用
class A
{
public:
A(const A &a){} //拷贝构造函数
A(const A &&a){} //移动构造函数
A& operator=(const A &a){} //拷贝赋值运算符
A& operator=(const A &&a){} //移动赋值运算符
~A(){} //析构函数
};
2.拷贝构造函数
定义:
构造函数的第一个参数为自身类类型的引用,且任何额外参数都有默认值。编译器生成的合成拷贝构造函数为浅拷贝。
使用拷贝构造函数的情况:
①用=定义变量时 //Ta = b;
②非引用参数传递 // fcn(T v); fcn(a); 这也是参数第一个为&的原因,否则出现死循环
③非引用函数返回 // fcn(){… return v;}
④列表初始化数组元素 // T arr[size] = {…}
⑤容器元素用非emplace添加 // insert()|push()
⑥直接初始化时编译器根据参数匹配调用 // T a(b);编译器使用普通函数匹配
3.拷贝赋值运算符
A a1,a2;
a1 = a2; //将一个对象赋予同类型的另一个对象
合成的拷贝赋值运算符类似合成的拷贝构造函数。
4.析构函数
当变量离开作用域、对象销毁时成员销毁、容器销毁时其元素销毁、delete对象指针,析构函数调用,成员按初始化的逆序销毁。
一个类自定义了析构函数,基本也需要自定义拷贝赋值运算符和拷贝构造函数,反过来却不一定。
5.移动构造函数和移动赋值运算符
从给定对象窃取资源而不是拷贝资源(指针的移交,之前的指针变量需置nullptr),一般均声明为noexcept(不抛异常,置于参数列表与初始化列表开始的:中间)
如果一个类定义了自己的拷贝构造函数、拷贝赋值运算符或析构函数,编译器就不会为它合成移动构造函数和移动赋值运算符了。
移动迭代器解引用生成一个右值引用,通过调用标准库的make_move_iterator函数可将一个普通迭代器转换为一个移动迭代器。copy(make_move_iterator(b),make_move_iterator(e),d);
6.简易的SmartPoint
template<typename T>
class SmartPoint
{
public:
SmartPoint():users(new size_t(1)),p(new T()){} //默认构造函数
explicit SmartPoint(T *q):users(new size_t(1)),p(q){} //内置指针构造函数
SmartPoint(const SmartPoint &sp):users(sp.users),p(sp.p){++*users;} //拷贝构造函数
SmartPoint& operator=(const SmartPoint &sp); //赋值运算符重载
~ SmartPoint(); //析构函数
private:
size_t *users;
T *p;
};
template<typename T>
SmartPoint<T>& SmartPoint<T>::operator=(const SmartPoint &sp)
{
++*sp.users; //先对右边指针引用计数+1,解决自我复制存在的问题
if (--*users==0)
{
delete users;
delete p;
}
users = sp.users;
p = sp.p;
return *this;
}
template<typename T>
SmartPoint<T>::~ SmartPoint()
{
if (--*users==0)
{
delete users;
delete p;
}
}
7.引用限定符
- 引用限定符可以为 &或&&(成员函数参数列表后),且必须同时出现在函数的声明和定义中。
- 可以和const结合使用,const限定符置前,引用置后,二者均可以作为重载区分。
- &表示仅能通过左值调用该函数,&&表示仅能通过右值调用该函数,const &均可。
- 如果一个成员函数有引用限定符,则具有相同参数列表的所有版本都必须有引用限定符。