什么是 C++ 中的移动语义?它的作用是什么?右值引用是什么?如何使用右值引用实现移动语义?
参考文献:利用移动语义优化 C++ 程序性能的实用指南_c++什么是移动语义-CSDN博客
定义
移动语义允许资源的“移动”而不是“拷贝”。在传统的 C++ 中,当一个对象被赋值或传递给函数时,通常会发生拷贝操作,这会导致性能下降,尤其是在处理大型对象时。移动语义通过引入右值引用和移动构造函数、移动赋值运算符,允许程序员将资源的所有权从一个对象转移到另一个对象,而不是进行深拷贝。
作用
旨在提高对象的移动效率和减少资源的不必要拷贝. 传统的拷贝构造函数和赋值运算符会在对象间进行深拷贝,即将原始对象的数据复制到一个新的对象中。而移动语义则允许我们将资源从一个对象转移到另一个对象,而无需进行深拷贝。
class Test{
public:
int* m_num;
Test():m_num(new int(100))
{
cout<<"构造函数"<<endl;
}
Test(const Test& a):m_num(new int(*a.m_num)){
cout<<"拷贝构造"<<endl;
}
~Test(){
delete m_num;
}
};
Test getobj(){
Test t;
return t;
}
int main(){
Test t=getobj();
cout<<"*t.m_num"<<endl;
return 0;
}
这段代码调用了两次拷贝构造,当getobj()函数里以值的形式返回时,调用一次拷贝构造,当main里将返回的临时变量赋值给对象t,调用一次拷贝构造(但是,由于编译器RVO优化,这段代码只输出一次拷贝构造)
class Test{
public:
int* m_num;
Test():m_num(new int(100))
{
cout<<"构造函数"<<endl;
}
Test(const Test& a):m_num(new int(*a.m_num)){
cout<<"拷贝构造"<<endl;
}
Test(Test&& a):m_num(*a.m_num){
a.m_num=nullptr;
cout<<"移动构造"<<endl;
}
~Test(){
delete m_num;
}
};
Test getobj(){
Test t;
return t;
}
int main(){
Test t=getobj();
cout<<"*t.m_num"<<endl;
return 0;
}
- 在上面的代码中添加了 移动构造函数(参数为右值引用类型),这样在进行 Test t=getObj();并没有调用构造函数进行深拷贝,而是调用的(浅拷贝)移动构造,提高了性能。
- 本例子中,getObj()返回值是一个右值,在进行赋值操作的时候如果等号 右边是一个右值,那么移动构造函数就会被调用。
右值引用
左值是指在内存中有明确的地址,我们可以找到这块地址的数据(可取地址,有名字)
右值是只提供数据,无法找到地址(不可取地址,没有名字)
可以使用move将左值转换为右值
实现移动语义
移动构造
#include<iostream>
using namespace std;
class A {
int* p;
public:
A(){
p = nullptr;
}
A(int* p) {
this->p = p;
cout << "构造函数"<<endl;
}
A(A&& other) {
this->p = other.p;
other.p = nullptr;
cout << "移动构造" << endl;
}
};
int main() {
A a(new int(30));
A b = move(a);
}
移动复制运算符
#include<iostream>
using namespace std;
class A {
int* p;
public:
A(){
p = nullptr;
}
A(int* p) {
this->p = p;
cout << "构造函数"<<endl;
} this->p = other.p;
A& operator=(A&& other) {
if (p) p = nullptr;
this->p = other.p;
other.p = nullptr;
cout << "赋值运算符" << endl;
return *this;
}
};
int main() {
A a(new int(30));
A b;
b= move(a);
}
输出结果
//构造函数
//赋值运算符