移动(move)语义C++引入了一种新的内存优化,以避免不必要的拷贝。在构造或者赋值的时候,如果实参是右值(或者左值由std::move转换成右值),便会匹配移动语义的函数调用如下述举例的Str(Str&& obj)。
Str(Str&& obj)
移动语义的本质是将资源(内存/句柄)转移给另一个对象,被转移资源的对象不应再被使用。(这个概念有点像仙侠小说中的夺舍,夺舍成功的人获取被夺舍的人的身体(资源)),如下面伪代码:
class Obj{ data Obj(){ data = malloc(100) } // 移动 (夺舍) Obj(Obj&& other){ data = other.data other.data = nullptr }}
class Obj
{
data
Obj(){
data = malloc(100)
}
// 移动 (夺舍)
Obj(Obj&& other){
data = other.data
other.data = nullptr
右值直观理解是等号右边的值(大概如此,并不准确),右值的概念指代的东西比较多,大概是指不可寻址的值(也有例外)。我觉得这个不必太过纠结,记住几个常见的即可:
在 C++11之后,C++根据
将值分为以下类别:
参考测试项目代码ModernCppTest/modrenc_rvalueref_stdmove.cpp 主要内容:
ModernCppTest/modrenc_rvalueref_stdmove.cpp
#include "ModernCppTestHeader.h"#include <string>using std::string;namespace n_rvalueref { class Str { public: Str() { LOG("无参构造"); this->str = new string(); } Str(const string& str) { LOG("有参构造 str = " << str); this->str = new string(str); } Str(const Str& obj) { LOG("拷贝构造 obj.str = " << *obj.str); this->str = new string(*obj.str); } Str(Str&& obj) noexcept { LOG("移动构造 obj.str = " << *obj.str); this->str = std::move(obj.str); // 被移动的对象不应该再被使用了 obj.str = nullptr; } Str& operator=(Str&& v) noexcept { LOG("移动语义 operator = "); if (this != &v) { this->str = std::move(v.str); } return *this; } Str operator+(const Str& v) { string s = *this->str + *v.str; return Str(s); } void Log() { LOG(str); } string* str; };}using n_rvalueref::Str;// 右值引用&移动语义void rvalueref_stdmove_test(){ LOG_FUNC(); LOG_TAG("拷贝构造"); { Str t1("A"); Str t2 = t1; LOG_VAR(*t2.str); } LOG_TAG("移动构造, 注意被移动的对象t1不应再被使用"); { // t1是左值,使用std::move强制转换成右值 Str t1("A"); Str t2 = std::move(t1); LOG_VAR(*t2.str); } LOG_TAG("移动语义的运算符重载,注意运算符重载发生赋值运算(这个例子),而不是构造运算(上个例子)"); { Str t1("A"); Str t2; t2 = std::move(t1); } LOG_TAG("除了上述显示使用std::move转换,常见的容易忽视的发生移动构造场合列举"); { LOG("---1 连续加法产生的临时对象,c++会尝试使用移动语义进行优化"); Str t1("A"); Str t2("B"); Str t3("C"); Str t4; t4 = t1 + t2 + t3; LOG("---2 函数返回的临时对象,c++会尝试使用移动语义进行优化"); auto f = []() { auto s = Str("Hi"); return s; }; Str t5 = f(); /* - 在容器中插入或删除元素:比如 std::vector::push_back,如果传递给它的是右值,它就会使用移动语义。 - 在标准库算法中:许多标准库算法,比如 std::sort,std::partition 等,在进行元素交换时会使用移动语义。 - 在 std::swap 中:std::swap 会使用移动语义来交换两个对象。 */ }}
#include "ModernCppTestHeader.h"
#include <string>
using std::string;
namespace n_rvalueref {
class Str {
public:
Str() {
LOG("无参构造");
this->str = new string();
Str(const string& str) {
LOG("有参构造 str = " << str);
this->str = new string(str);
Str(const Str& obj) {
LOG("拷贝构造 obj.str = " << *obj.str);
this->str = new string(*obj.str);
Str(Str&& obj) noexcept {
LOG("移动构造 obj.str = " << *obj.str);
this->str = std::move(obj.str);
// 被移动的对象不应该再被使用了
obj.str = nullptr;
Str& operator=(Str&& v) noexcept {
LOG("移动语义 operator = ");
if (this != &v) {
this->str = std::move(v.str);
return *this;
Str operator+(const Str& v)
string s = *this->str + *v.str;
return Str(s);
void Log()
LOG(str);
string* str;
};
using n_rvalueref::Str;
// 右值引用&移动语义
void rvalueref_stdmove_test()
LOG_FUNC();
LOG_TAG("拷贝构造");
Str t1("A");
Str t2 = t1;
LOG_VAR(*t2.str);
LOG_TAG("移动构造, 注意被移动的对象t1不应再被使用");
// t1是左值,使用std::move强制转换成右值
Str t2 = std::move(t1);
LOG_TAG("移动语义的运算符重载,注意运算符重载发生赋值运算(这个例子),而不是构造运算(上个例子)");
Str t2;
t2 = std::move(t1);
LOG_TAG("除了上述显示使用std::move转换,常见的容易忽视的发生移动构造场合列举");
LOG("---1 连续加法产生的临时对象,c++会尝试使用移动语义进行优化");
Str t2("B");
Str t3("C");
Str t4;
t4 = t1 + t2 + t3;
LOG("---2 函数返回的临时对象,c++会尝试使用移动语义进行优化");
auto f = []() {
auto s = Str("Hi");
return s;
Str t5 = f();
/*
- 在容器中插入或删除元素:比如 std::vector::push_back,如果传递给它的是右值,它就会使用移动语义。
- 在标准库算法中:许多标准库算法,比如 std::sort,std::partition 等,在进行元素交换时会使用移动语义。
- 在 std::swap 中:std::swap 会使用移动语义来交换两个对象。
*/
原文链接:https://www.cnblogs.com/hggzhang/p/17523881.html
本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728