移动语义是 C++11 标准引入的一个特性,旨在通过优化资源管理改善 C++ 的效率。移动语义主要涉及通过右值引用(rvalue references)来避免不必要的深拷贝,从而减少资源的分配和释放。
1. 拷贝场景
在 C++ 中,有很多的编码场景下,会导致对象的拷贝(拷贝构造函数、拷贝赋值函数)。特别是针对临时对象、即将废弃的对象的拷贝,将会极大降低程序的效率。

#if 1
#include <iostream>
#include <vector>
using namespace std;
class Demo
{
public:
Demo(int n)
{
m_size = n;
p_arr = (int*)malloc(sizeof(int) * m_size);
if (nullptr == p_arr)
{
return;
}
for (int i = 0; i < 10; ++i)
{
p_arr[i] = 100 + i;
}
cout << "默认构造" << endl;
}
Demo(const Demo& demo)
{
m_size = demo.m_size;
p_arr = (int*)malloc(sizeof(int) * m_size);
if (nullptr == p_arr)
{
return;
}
for (int i = 0; i < 10; ++i)
{
p_arr[i] = demo.p_arr[i];
}
cout << "拷贝构造" << endl;
}
Demo& operator=(const Demo& demo)
{
if (this == &demo)
{
return *this;
}
m_size = demo.m_size;
if (p_arr != nullptr)
{
delete[] p_arr;
p_arr = nullptr;
}
p_arr = (int*)malloc(sizeof(int) * m_size);
if (nullptr == p_arr)
{
return *this;
}
for (int i = 0; i < 10; ++i)
{
p_arr[i] = demo.p_arr[i];
}
cout << "拷贝赋值" << endl;
return *this;
}
~Demo()
{
if (p_arr != nullptr)
{
delete[] p_arr;
p_arr = nullptr;
}
cout << "析构函数" << endl;
}
public:
int* p_arr;
int m_size;
};
Demo create_demo()
{
Demo demo1(10);
Demo demo2(15);
if (demo1.m_size > demo2.m_size)
{
return demo1;
}
return demo2;
}
// 1. 返回局部对象
void test01()
{
// create_demo 返回临时对象
// 在销毁之前,需要调用拷贝构造函数
Demo demo = create_demo();
}
// 2. 值方式传参
void do_logic(const Demo& demo) {}
void test02()
{
// create_demo 返回临时对象
// 在销毁之前,会创建临时对象赋值给 do_logic 的 demo 参数
do_logic(create_demo());
}
// 3. 容器操作
void test03()
{
vector<Demo> vec;
// 将临时对象拷贝到容器中
vec.push_back(Demo(10));
}
// 4. 交换两个对象
void test04()
{
Demo demo1(10);
Demo demo2(20);
swap(demo1, demo2);
}
// 5. 对象赋值
void test05()
{
Demo demo(10);
// 将对象拷贝赋值给 demo 对象
demo = create_demo();
}
// 6. 容器中元素赋值
void test06()
{
vector<Demo> vec;
vec.push_back(Demo(10));
vec[0] = Demo(10);
}
int main()
{
cout << "----test01----" << endl;
test01();
cout << "----test02----" << endl;
test02();
cout << "----test03----" << endl;
test03();
cout << "----test04----" << endl;
test04();
cout << "----test05----" << endl;
test05();
cout << "----test06----" << endl;
test06();
return 0;
}
#endif
程序执行结果:
----test01---- 默认构造 默认构造 拷贝构造 析构函数 析构函数 析构函数 ----test02---- 默认构造 默认构造 拷贝构造 析构函数 析构函数 析构函数 ----test03---- 默认构造 拷贝构造 析构函数 析构函数 ----test04---- 默认构造 默认构造 拷贝构造 拷贝赋值 拷贝赋值 析构函数 析构函数 析构函数 ----test05---- 默认构造 默认构造 默认构造 拷贝构造 析构函数 析构函数 拷贝赋值 析构函数 析构函数 ----test06---- 默认构造 拷贝构造 析构函数 默认构造 拷贝赋值 析构函数 析构函数
如果我们能够将这些临时对象、或者即将废弃的对象所持有的资源进行转移、而不是拷贝,这将大大提高程序的效率。

2. 右值引用
要实现针对临时对象(右值)的特殊拷贝操作,首要一点,就是要区分左值(持久对象)对象和右值对象。在 C++11 之前,只有左值引用、常量引用(万能引用),无法精确匹配到右值场景。所以,在 C++11 中,增加了专门针对右值的右值引用,使用 && 语法表示,从而辅助实现资源移动语义。
#if 1
#include <iostream>
using namespace std;
class Demo
{
public:
Demo(int n)
{
cout << "默认构造" << endl;
}
// 既能匹配左值对象、又能匹配右值对象
// 即:无法区分左值、右值,也就无法实现分别处理
Demo(const Demo& demo)
{
cout << "拷贝构造" << endl;
}
Demo& operator=(const Demo& demo)
{
cout << "拷贝赋值" << endl;
return *this;
}
~Demo()
{
cout << "析构函数" << endl;
}
public:
int* p_arr;
int m_size;
};
void test()
{
// 左值引用
Demo demo(10);
Demo& r1 = demo;
// 常量引用(万能引用)
// Demo& r2 = Demo(20); // 非常量引用的初始值必须为左值
const Demo& r3 = Demo(20);
// 右值引用
Demo&& r4 = Demo(20);
// Demo&& r5 = demo; // 无法将右值引用绑定到左值
}
int main()
{
test();
return 0;
}
#endif
3. 资源移动
移动语义是一种机制,它允许开发者“移动”资源(如动态内存、文件句柄等),而不是复制资源。当对象的资源被移动时,源对象的资源所有权被转移到目标对象,源对象则被置于一种有效但未指定的状态。移动语义通过移动构造函数和移动赋值运算符实现。为了能够匹配临时对象,可以在类中增加:
- 参数为右值引用的构造函数(移动构造函数)
- 参数为右值引用的赋值函数(移动赋值函数)
// 移动构造 Demo(Demo&& demo) // 移动赋值 Demo& operator=(Demo&& demo)
在这两个函数编写资源转移的相关代码。
注意两点:
- 参数为右值引用,而非常量右值引用
- 如果一个左值不再使用,也可以使用 move 函数将左值转换为右值对象,实现资源转移
#if 1
#include<iostream>
#include <vector>
using namespace std;
class Demo
{
public:
Demo(int n)
{
m_size = n;
p_arr = (int*)malloc(sizeof(int) * m_size);
if (nullptr == p_arr)
{
return;
}
for (int i = 0; i < 10; ++i)
{
p_arr[i] = 100 + i;
}
cout << "默认构造" << endl;
}
Demo(const Demo& demo)
{
m_size = 0;
p_arr = nullptr;
m_size = demo.m_size;
p_arr = (int*)malloc(sizeof(int) * m_size);
if (nullptr == p_arr)
{
return;
}
for (int i = 0; i < 10; ++i)
{
p_arr[i] = demo.p_arr[i];
}
cout << "拷贝构造" << endl;
}
Demo& operator=(const Demo& demo)
{
if (this == &demo)
{
return *this;
}
m_size = demo.m_size;
if (p_arr != nullptr)
{
delete[] p_arr;
p_arr = nullptr;
}
p_arr = (int*)malloc(sizeof(int) * m_size);
if (nullptr == p_arr)
{
return *this;
}
for (int i = 0; i < 10; ++i)
{
p_arr[i] = demo.p_arr[i];
}
cout << "拷贝赋值" << endl;
return *this;
}
/**************资源转移********************/
Demo(Demo&& demo)
{
// 获得 demo 资源所有权
p_arr = demo.p_arr;
m_size = demo.m_size;
// 移除 demo 资源所有权
demo.p_arr = nullptr;
demo.m_size = 0;
cout << "移动构造" << endl;
}
Demo& operator=(Demo&& demo)
{
if (p_arr != nullptr)
{
delete[] p_arr;
p_arr = nullptr;
m_size = 0;
}
// 获得 demo 资源所有权
p_arr = demo.p_arr;
m_size = demo.m_size;
// 移除 demo 资源所有权
demo.p_arr = nullptr;
demo.m_size = 0;
cout << "移动赋值" << endl;
return *this;
}
/**********************************/
~Demo()
{
if (p_arr != nullptr)
{
delete[] p_arr;
p_arr = nullptr;
}
cout << "析构函数" << endl;
}
public:
int* p_arr;
int m_size;
};
Demo create_demo()
{
Demo demo1(10);
Demo demo2(15);
if (demo1.m_size > demo2.m_size)
{
return demo1;
}
return demo2;
}
// 1. 返回局部对象
void test01()
{
// create_demo 返回临时对象
// 在销毁之前,需要调用拷贝构造函数
Demo demo = create_demo();
}
// 2. 值方式传参
void do_logic(const Demo& demo) {}
void test02()
{
// create_demo 返回临时对象
// 在销毁之前,会创建临时对象赋值给 do_logic 的 demo 参数
do_logic(create_demo());
}
// 3. 容器操作
void test03()
{
vector<Demo> vec;
// 将临时对象拷贝到容器中
vec.push_back(Demo(10));
}
// 4. 交换两个对象
void test04()
{
Demo demo1(10);
Demo demo2(20);
swap(demo1, demo2);
}
// 5. 对象赋值
void test05()
{
Demo demo(10);
// 将对象拷贝赋值给 demo 对象
demo = create_demo();
}
// 6. 容器中元素赋值
void test06()
{
vector<Demo> vec;
vec.push_back(Demo(10));
vec[0] = Demo(10);
}
// 7. 即将销毁的对象
void test07()
{
Demo demo(10);
vector<Demo> vec;
vec.push_back(move(demo));
cout << "旧对象:" << demo.p_arr << " " << demo.m_size << endl;
}
int main()
{
cout << "----test01----" << endl;
test01();
cout << "----test02----" << endl;
test02();
cout << "----test03----" << endl;
test03();
cout << "----test04----" << endl;
test04();
cout << "----test05----" << endl;
test05();
cout << "----test06----" << endl;
test06();
cout << "----test07----" << endl;
test07();
return 0;
}
#endif


冀公网安备13050302001966号