C++11新特性(右值引用、移动语义、资源管理、智能指针)
右值引用
引例
std::vector<String> v;
v.push_back("hello, world");
//调用String(const char *)、String(const String &)、~String()
//push字符串的过程中会产生临时对象,会调用构造函数、复制构造函数、析构函数
问题症结
临时对象的构造和析构带来了不必要的资源拷贝,效率低下,右值引用的机制可以在语法层面上识别出临时对象,在使用临时对象构造新对象时,将临时对象所持有的资源转移到新的对象中,就能消除这种不不必要的拷贝。
左值、右值
-
区分 :能对表达式进行取地址的就是左值,否则为右值
-
左值引用:
- 非常量左值引用:只能绑定到非常量左值
int ia = 10; int & a = ia;
- 常量左值引用:可以绑定到所有类型的值,包括非常量左值、常量左值、右值
const int ib = 30; int &b = ib;
const int &ri = 20;//绑定到右值
- 非常量左值引用:只能绑定到非常量左值
-
右值引用:int && ref;
-
左值引用可以绑定到左值:int x ; int xr = x;
-
非常量左值引用不可以绑定到右值: int &r = 0; //error
-
常量左值引用可以绑定到右值和左值
const int & xr = x; const int & xr = 0
-
右值引用可以绑定到右值:
int && r = 0;
-
右值引用不可以绑定到左值:
int x; int && xr = x; //error
-
常量右值引用没有显式意义
-
移动语义(std::move)
- 编译器只对右值引用才能调用转移构造函数和转移赋值函数,而所有命名对象都只能是左值引用,如果已知一个明明对象不再被使用而想对它调用转移构造函数和转移赋值函数,std::move 就是把一个左值引用当作右值引用来使用。
- 对于右值引用而言,其本身不是右值,因为可以取地址。
资源管理(RAII)
RAII的本质是用栈对象来管理资源,因为栈对象在离开作用域时,会自动调用析构函数
- 构造时初始化资源
- 析构时释放资源
智能指针
std::auto_ptr(不再使用)
- 在拷贝构造或赋值操作时,会发生所有权的转移
std::unique_ptr
是一个独享所有权的智能指针,它提供一种严格语义上的所有权
- 无法进行复制、赋值操作
std::unique_ptr <int> str1(new int(99));
std::unique_ptr <int> str2(str1);//error
- 可以进行移动构造和移动赋值操作
unique_ptr<int> str1(new int(99));
unique_ptr<int> str2 = std::move(str1);//把str1指向的内存转给str2,str1不再拥用该内存
- 具有移动语义,可作为容器元素
unique_ptr<int> str1(new int(99));
vector<unique_ptr<int>> vec;
vec.push_back(std::move(str1));
vec.push_bacK(str1);//error
std::shared_ptr
是一个引用计数智能指针,用于共享对象的所有权 use_count()
- 引进了一个计数器shared_count用来表示当前有多少个智能指针对象共享指针指向的内存块
- 析构函数中不是直接释放只针对应的内存块,如果shared_count>0则不释放内存只是将引用计数-1,只有等于0时才释放内存。
- 复制构造与赋值操作符只是提供一般意义上的复制功能,并且将引用计数+1
- 循环引用会导致内存泄漏----->采用weak_ptr
- 若成员函数的返回值shared_ptr,则返回的时候采用shared_from_this(),但先要继承std::enable_shared_from_this
return shared_ptr<T>(this)
return shared_from_this();
std::weak_ptr
- std::shared_ptr是强引用智能指针
- std::weak_ptr是弱引用智能指针
- 强引用,只要有一个引用存在,对象就不能被释放
- 弱引用,并不增加对象的引用计数,但它直到对象是否存在。
- 如果存在,提升为shared_ptr成功,否则,提升失败
lock()
- 通过weak_ptr访问对象的成员时,要提升为shared_ptr;