C++11之move移动语义
最近在学move语义,在网上搜索到的文章看不懂,左值引用、右值引用...搞的好乱,问了一下deepseek,举了几个例子,然后我好像懂了move的大致用法,当然右值引用还是不懂,本文章大概总结一下关于move的用法,当做笔记了hhh(根据deepseek的回答总结出来的),很浅,如果想深入理解move可以跳过。
通俗解释 move 语义
想象一下你有一本书,你现在想把它给你的朋友。
不用 move(拷贝): 你跑去复印店,把整本书复印一份,然后把复印本给你的朋友。你自己还留着原版书。这个过程(复印)既费钱(消耗内存)又费时间(消耗CPU周期)。
用 move(移动): 你直接走过去,把原版书递到你朋友手里。这本书的所有权就从你转移到了你朋友。你手上不再拥有这本书(但它处于一个“有效但空”的状态,比如你可以再买一本新的放回手里)。
移动语义的核心思想就是“所有权转移”,它避免了不必要的深层复制,极大地提升了性能,尤其是对于管理着大量资源的对象(如字符串、向量、文件句柄等)。
1、用于容器操作
最常见的场景,用于高效地将元素放入或移出容器。
#include <iostream>
#include <queue>
#include <functional>using namespace std;int main() {queue<function<void()>> q;// 向队列中添加一个任务q.push([]() { cout << "Hello from task!\n"; });cout<<"q.size()="<<q.size()<<endl;cout<<"q.front()的调用:";q.front()(); //调用队列中的第一个函数// 正确(高效)的方式:移出任务function<void()> task = move(q.front()); // 这里用 move!cout<<"task的调用:";task(); // 执行任务cout<<"-------------------------"<<endl;//此时队列大小仍为1,但是队首元素已不可用,因为被转移了cout<<"q.size()="<<q.size()<<endl;//如果强行使用,会抛出异常try{q.front()();}catch(const std::exception& e){std::cerr << e.what() << '\n';}return 0;
}
输出:
2、拷贝对象
#include <iostream>
#include <vector>
#include <functional>using namespace std;//使用move
void test01(){std::vector<int> v1 = {1, 2, 3};std::vector<int> v2 = std::move(v1);// 现在 v1 不再是 {1,2,3} 了,它很可能是一个空向量cout<<"v1:";for(int& i:v1){cout<<i<<' ';}cout<<endl;cout<<"v2:";for(int& i:v2){cout<<i<<' ';}cout<<endl;
}//不使用move
void test02(){std::vector<int> v1 = {1, 2, 3};std::vector<int> v2 = v1;// 现在 v1 仍是 {1,2,3} cout<<"v1:";for(int& i:v1){cout<<i<<' ';}cout<<endl;cout<<"v2:";for(int& i:v2){cout<<i<<' ';}cout<<endl;//将v2下标为1的元素修改为100v2[1]=100;cout<<"v1:";for(int& i:v1){cout<<i<<' ';}cout<<endl;cout<<"v2:";for(int& i:v2){cout<<i<<' ';}cout<<endl;
}int main(int argc, const char *argv[]){test01();cout<<"--------------"<<endl;test02();return 0;
}
输出:
3、实现高性能的swap
template<class T>
void swap(T& a, T& b) {T temp = std::move(a); // a 的资源移动给 tempa = std::move(b); // b 的资源移动给 ab = std::move(temp); // temp 的资源(原是a的)移动给 b
}
// 整个过程没有任何昂贵的拷贝,只有三次低成本的所有权转移。
4、构造函数的初始化列表中初始化类成员
#include<iostream>
using namespace std;
#include<functional>class MyClass {std::string m_name;
public:// 使用移动语义来初始化成员,避免拷贝MyClass(std::string name) : m_name(std::move(name)) {// 此时参数 name 已被移空,不能再使用它cout<<"name: "<<name<<endl;}void printInfo(){cout<<"m_name: "<<m_name<<endl;}
};int main(int argc, char const *argv[]){std::string s = "Hello";MyClass obj(s); // 构造 obj 时,s 被拷贝给参数 name,然后 name 被移动给 m_nameobj.printInfo();return 0;
}
输出: