一、左值和右值定义(能否取地址)
1.左值:可以取地址的对象
2.右值:不可以取地址、临时要销毁的对象
二、左值引用
1.定义:对左值的引用
int& ra = a;
2.作用:传递参数和返回值时减少不必要的拷贝
三、右值引用
1.定义:对右值的引用
// 以下是对几种右值的右值引用
int&& rr1 = 10;
double&& rr2 = x + y;
double&& rr3 = fmin(x, y);
2.作用
(1)移动语义:减少两个对象交互时不必要的拷贝,节省运算存储资源,提高效率
(2)完美转发:更加简洁准确地定义泛型函数
四、移动语义中的移动构造函数
定义:移动构造函数是一个接受右值引用参数的构造函数,它用于将资源从一个对象转移到另一个对象。移动构造函数的实现通常会“窃取”源对象的资源,并将源对象的资源指针(或其他表示资源的成员)置为空,从而确保源对象不再拥有这些资源。
class MyObject {
public:
// 移动构造函数
MyObject(MyObject&& other) noexcept {
// 窃取资源
resource_ = other.resource_;
other.resource_ = nullptr; // 将源对象的资源指针置为空
}
// 其他成员和方法...
private:
Resource* resource_; // 假设Resource是一种动态分配的资源类型
};
五、完美转发
泛型函数是一种能够在不指定具体类型的情况下工作的函数,它们通常使用模板来实现。泛型函数允许在编写代码时延迟类型的指定,使得函数可以处理各种不同类型的数据。
右值引用可以更简洁明确地定义泛型函数,因为它们提供了对右值的特定支持,使得函数能够在不必要的拷贝的情况下处理临时对象。在C++11之前,泛型函数可能会导致对传递的参数进行不必要的深拷贝,特别是对于临时对象(右值)的处理。而右值引用使得我们可以针对右值和左值分别进行优化,从而避免了不必要的资源拷贝。
举例来说,考虑一个简单的模板函数,该函数接受一个参数并返回其两倍值:
template<typename T>
T doubleValue(T value) {
return value * 2;
}
在这个函数中,无论传递的是左值还是右值,参数都会被复制一次,这可能会导致性能损失,特别是对于大型对象或临时对象而言。
通过使用右值引用,我们可以更明确地指定函数接受右值参数,并对其进行优化:
template<typename T>
T doubleValue(T&& value) {
return value * 2;
}
现在,这个函数可以接受左值和右值,并根据参数类型进行适当的优化。如果传递的是右值,那么不会发生额外的拷贝,因为右值引用允许我们在需要时直接访问临时对象。这使得函数更加灵活和高效,同时也更清晰地表达了函数的意图,即该函数可以接受任意类型的参数,并返回其两倍值,而不会产生不必要的开销。
六、浅拷贝和深拷贝(有无资源的重新分配)
1.浅拷贝只复制指针或引用,将源对象的值拷贝到目标对象中去,本质上来说源对象和目标对象共用一份实体,只是所引用的变量名不同,地址其实还是相同的,因此复制后的对象共享相同的资源。
2.拷贝的时候先开辟出和源对象大小一样的空间,然后将源对象里的内容拷贝到目标对象中去,这样两个指针就指向了不同的内存位置,并且里面的内容是一样的。
class ShallowCopy {
public:
int* data;
// 构造函数
ShallowCopy(int val) {
data = new int(val);
}
// 拷贝构造函数(浅拷贝)
ShallowCopy(const ShallowCopy& other) {
data = other.data; // 只复制指针,而不是分配新的内存
}
// 拷贝构造函数(深拷贝)
DeepCopy(const DeepCopy& other) {
data = new int(*other.data); // 分配新的内存并复制数据
}
// 析构函数
~ShallowCopy() {
delete data;
}
};
标签:右值,对象,左值,引用,拷贝,构造函数
From: https://www.cnblogs.com/fly-smart/p/18017891