- 原来C++类中,有6个默认成员函数: (默认成员函数就是我们不写编译器会生成一个默认的)
- 构造函数
- 析构函数
- 拷贝构造函数
- 拷贝赋值重载
- 取地址重载(用处不大)
- const 取地址重载(用处不大)
- C++11中新增了【移动构造函数】和【移动赋值运算符重载】,针对这两个新增函数有一些需要注意的点如下:
一.相关知识点传送门(移动语义&右值引用)
- C++11中新增了【移动构造函数】和【移动赋值运算符重载】,针对这两个新增函数主要涉及到【移动语义&右值引用】,具体的知识点在以下几篇博客中,传送门如下:
【C++11特性篇】右值引用变量的属性会被编译器识别成左值【详解&证明&代码演示】
【C++11特性篇】一文助小白轻松理解 C++中的【左值&左值引用】【右值&右值引用】
【C++11特性篇】探究【右值引用(移动语义)】是如何大大提高效率?——对比【拷贝构造&左值引用】
二.移动构造函数
- C++11中新增了【移动构造函数】和【移动赋值运算符重载】,针对这两个新增函数有一些需要注意的点如下:
- 如果你没有自己实现移动构造函数,且没有实现 析构函数 、拷贝构造、拷贝赋值重载 中的任
意一个。那么编译器会 自动生成一个默认移动构造。- 默认生成的移动构造函数:
- 对于 内置类型成员会执行逐成员按字节拷贝,
- 自定义类型成员,则需要看这个成员 是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造。
- 为什么一定是没有实现 析构函数 、拷贝构造、拷贝赋值重载 中的任意一个,编译器才会生成默认的移动构造呢?
- 分析:因为这三个函数需要的场景往往是一致的
- 例如: 需要深拷贝(开空间)的类 ,例如vector,list就需要自己手动写【析构函数 、拷贝构造、拷贝赋值重载】
- 但是像不需要深拷贝的类,例如迭代器,则让编译器自己生成即可
三.移动运算符重载
- 移动赋值运算符重载同理:如果你没有自己实现移动赋值重载函数,且没有实现 析构函数 、拷贝构造、拷贝赋值重载 中的任意一个,那么编译器会自动生成一个默认移动赋值。
- 默认生成的移动构造函数
- 对于内置类型成员会执行逐成员按字节拷贝
- 自定义类型成员,则需要看这个成员是否实现移动赋值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造完全类似)
四.强制生成默认函数的关键字default
- 根据以上2,3点,我们知道:
- 没有实现 析构函数 、拷贝构造、拷贝赋值重载 中的任意一个,编译器才会生成默认的移动构造
- 如果我们已经自己生成了三个函数中的某一个,这时编译器不再生成默认的移动构造,但是我们依旧希望他生成,该怎么操作?
- 如下面代码场景所示:
Person(Person&& p) = default;
class Person { public: Person(const char* name = "", int age = 0) :_name(name) , _age(age) {} Person(const Person& p) //我们手动写了拷贝构造 :_name(p._name) , _age(p._age) {} Person(Person&& p) = default; //此时移动构造仍然会生成 private: bit::string _name; int _age; }; int main() { Person s1; Person s2 = s1; Person s3 = std::move(s1); return 0; }
五.禁止生成默认函数的关键字delete
- 只需在该函数声明加上=delete即可,该语法指示编译器不生成对应函数的默认版本,称 =delete修饰的函数为 删除函数
- 如下面代码场景所示:`Person(Person&& p) = delete;
class Person { public: Person(const char* name = "", int age = 0) :_name(name) , _age(age) {} //此时我们没有写析构函数 、拷贝构造、拷贝赋值重载 ,编译器理应默认生成移动构造 Person(Person&& p) = delete; //此时移动构造不会生成 private: bit::string _name; int _age; }; int main() { Person s1; Person s2 = s1; Person s3 = std::move(s1); return 0; }