背景:在程序运行的过程中,会产生大量的临时对象。临时对象只在某一个表达式执行的过程中创建,表达式执行完毕时,临时对象马上就被销毁。其作用是用来进行过渡的,造成了资源拷贝的不必要的浪费。
需求:希望将临时对象直接转移到新对象中
问题:临时对象是右值,只能通过 const 引用来绑定,但const引用同时又可以绑定到左值。C++11之前,当传递参数时,const引用是无法判断传递过来的参数是左值还是右值(所以const引用又称万能引用)。需要在语法层能够唯一确定传递过来的参数是右值。
解决:C++11 右值引用,并增加移动语义函数。
2、左值、右值-
左值:可以取地址的表达式,
-
右值:不能取地址的表达式,临时对象、匿名对象、临时变量、字面值常量都是右值
右值引用:只能绑定到右值不能绑定到左值。右值引用有名字的是左值,无名字的是右值。也可以说作为函数返回值的 && 是右值,声明出来的 && 是左值,即引用在等号左边是左值,在右边是右值。
int && ref = 10; //正确 int && ref = a; //error
std::move
添加编译选项: -fno-elide-constructors
唯一功能:强制将左值转换为右值,等同于 static_cast
std::move()作用于内置类型没有任何作用,内置类型本身是左值还是右值,经过移动后不会改变,对于自定义类型效果不错。
4、移动语义函数移动语义可以将资源通过浅拷贝方式从一个对象转移到另一个对象,只是转移,没有内存拷贝,这样能够减少不必要的临时对象的创建、拷贝以及销毁。
传递右值,优先调用移动语义的函数,即移动语义函数的执行优先于复制控制语义函数的执行。
移动构造函数
- 浅拷贝
- 转以后将其置为 nullptr
// 移动构造函数:右值引用作为函数参数
// 如果使用右值引用,意味着被修改,因为右值马上就要被销毁
String(String &&rhs)
: _pstr(rhs._pstr) { // 1、浅拷贝
// 2、转移后,将其设为nullptr
// 防止临时对象被销毁时,把它所拥有的资源回收了,不能达到转移资源的目的
rhs._pstr = nullptr;
cout << "String(String &&)" << endl;
}
移动赋值运算符函数
- 自移动
- 释放左操作数
- 浅拷贝
- 返回 this 指针
// 移动赋值运算符函数
String &operator=(String &&rhs) {
// rhs绑定的是一个右值,但rhs本身在函数内部是左值(因为其有名字)
cout << "String &operator=(String &&)" << endl;
//1、自移动
if(this != &rhs) {
//2、释放左操作数
delete [] _pstr;
_pstr = nullptr;
//3、浅拷贝
_pstr = rhs._pstr;
rhs._pstr = nullptr;
}
//4、返回*this
return *this;
}
实现内置类型 string 类
#include5、forward 转发#include #include using std::cout; using std::endl; using std::vector; class String { public: String() : _pstr(new char[1]()) { cout << "String()" << endl; } String(const char * pstr) : _pstr(new char[strlen(pstr) + 1]()) { strcpy(_pstr, pstr); cout << "String(const char*)" << endl; } // 拷贝构造函数:const万能引用,无法分辨左值和右值 String(const String & rhs) : _pstr(new char[strlen(rhs._pstr) + 1]()) { strcpy(_pstr, rhs._pstr); cout << "String(const String &)" << endl; } // 赋值运算符函数 String & operator=(const String & rhs) { cout << "String & operator=(const String&)" << endl; if(this != &rhs) { delete [] _pstr; _pstr = new char[strlen(rhs._pstr) + 1](); strcpy(_pstr, rhs._pstr); } return *this; } // 移动构造函数 String(String && rhs) : _pstr(rhs._pstr) { rhs._pstr = nullptr; cout << "String(String&&)" << endl; } // 移动赋值运算符函数 String & operator=(String && rhs) { cout << "String& operator=(String&&)" << endl; if(this != &rhs) { delete [] _pstr; _pstr = rhs._pstr; rhs._pstr = nullptr; } return *this; } ~String() { if(_pstr){ delete [] _pstr; _pstr = nullptr; } cout << "~String()" << endl; } friend std::ostream & operator<<(std::ostream &os, const String&); private: char * _pstr; }; std::ostream & operator<<(std::ostream &os, const String& rhs) { os << rhs._pstr; return os; }
参数在传递过程中保持其值属性的功能,即在参数传递过程中,左值传递后仍是左值,右值仍是右值,根据参数的类型转发给正确的函数。
6、emplace_back就地构造对象,只调用一次构造函数,避免了内存的拷贝和移动。
push_back(); // 两次构造(临时对象创建,容器内部创建),一次析构(临时对象析构)。 emplace_back(); // 一次构造(容器内直接创建)



