c++之移动构造函数或者移动赋值运算符的作用详解
目录
- 原理介绍
- 注意事项
- 思考
- 总结
原理介绍
前面的文章中有的涉及到了移动构造函数或者移动赋值运算符,对于它们的形式有了一定的了解,但是对他们的核心作用以及为什么要引入这两个东东,很多朋友还是一知半解。本文就是来解决这个问题的。
要理解这个问题绕不开右值引用,c++11之前的 版本中只有拷贝构造函数,而拷贝构造函数是一般会深拷贝,即会创建两个完全一样的对象,包括指针指向的区域都会进行重新申请内存和拷贝。这种情况下如果对象是一个大数组或占用资源多的对象,就会产生很大的内存拷贝开销。那么如何解决这个问题呢?
c+11版本引入了右值引用、移动语义来解决这个问题。这里先说结论:通过移动构造函数或移动赋值运算符产生的对象与元对象会产生一个资源的转移。举个形象的例子:小明手里有个篮球,小王手里没有篮球,以前的拷贝构造函数或者拷贝赋值运算符重载的结果是小王会按照小明手里的篮球重新买一个;而移动构造函数和移动赋值运算符重载实现则更像是小王把小明手里的篮球放到自己手里,而小明手里没有了篮球。
从上面的例子编程客栈可以看出移动构造函数或移动赋值运算符的作用就是通过资源管理权转移的方dlqZmpm式实现对象的构造,使得可以减少内存拷贝的开销。
移动构造函数的的参数一定是一个右值引用。
下面是一个给出一个移动构造函数的典型的例子:
class MyClass { std::string str; //一个类成员 int* ptr; //一个指针成员 public: A(){} A(MyClass && a)::str(std::move(a.str)) { } A operator=(MyClass&& a) //移动赋值运算符重载 { if(this != &a) { 编程 str = std:move(a.str); //这里因为str::string类已经实现了移动构造赋值运算符,std::string作为一个类已经支持了这个,所以可以这样做,如果是 ptr = a.ptr; //需要自己去切换指针所指向的区域 a.ptr = nullptr; } } }
注意事项
- 一个类的移动构造函数或移动赋值运算符的实现是由类自己实现的
- 如果要使用移动构造函数或者移动赋值运算符,那么用户必须显式的定义移动构造函数和移动赋值运算符的重载
- 一些c++标准库中提供的类很多都已经实现了移动构造函数和移动赋值运算符的重载,例如std::string、stl容器类、智能指针(std::shared_ptr<T>、std::unique_ptr<T>)等
- 用户自己定义类在有需要的情况下需要在自定义的类中实现移动构造函数和移动赋值运算符重载。
- 移动构造函数或移动赋值运算符重载实现基于右值引用,一定是以右值引用作为参数的
思考
移动构造函数本质是资源管理权的转移,那如果跟std::shared_ptr<T>会发生什么?
- 请看下面的代码:
class MyClass { public: A(){ std::cout<<"A的构造函数"<<std::endl; } ~A(){ js std::cout<<"A的析构函数"<<std::endl; } } class Container { private: std::shared_ptr<MyClass>js ptr; public: Container(Container&& container):ptr(std::move(container.ptr)) { std::cout<<"移动构造函数"<<std::endl; } Container operator=(Container&& container) { if(this != &container) //这里不能少 { ptr = std::move(container.ptr); //这里也可以,因为std::shared_ptr内部实习拿了移动赋值运算符 } } }
shared_ptr 的移动操作:
- 转移资源所有权
- 源指针置空
- 保持引用计数不变
- 是线程安全和异常安全的
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程客栈(www.devze.com)。
精彩评论