Administrator
Administrator
发布于 2025-09-25 / 5 阅读
0
0

左值与右值

左值

  • 可以出现在赋值表达式左边的对象,一般为变量、数组元素、解引用后的指针等

  • 具有名称的、指向内存空间的标识符

  • 可以执行取地址操作的对象

	int i = 10; //i为左值

	int arr[5];
	arr[0] = 20; //arr[0]为左值

	int* x = new int(30);
	*x = 40; // *x为左值

特殊的左值

    int i = 10;
	++i = 20; // 前置自增++i可以作为左值,此时i的值就是20
	int* p = &++i; //前置自增也可以取地址,和&i的结果是一样的


	auto p2 = &"abc"; //取出静态数据区的字符串常量的指针

右值

出现在赋值运算符右侧的值,一般为字面量、表达式等

	int i = 10; //10为右值
	const char* c = "abc"; // "abc"右值
	i = 10 + 50; // 10+50为右值

左值引用T&

  • 左值引用就是引用,它是对变量的借用,是变量的别名

  • 引用不管理原有变量的生命周期

  • 声明时必须指向一个对象,并且后续不能重新指向为其它对象

  • 在函数传参时不希望发生值拷贝带来的较大的消耗时,可以用左值引用,这非常常见

void func(const int& i) {
    cout << &i << endl;
}


int main() 
{
    int i = 10;
    int& ri = i;
    ri = 10;

    func(i); //不会发生值拷贝,形参i和调用者i是相同地址

    return 0;
}

右值引用T&&

可以绑定到右值,实现移动语义(在语义上与拷贝进行区分,一目了然的知道是一个即将被移动的对象),允许对右值进行移动而非拷贝,实际的移动逻辑在类的移动构造函数移动赋值运算符中实现,而非类类型用右值引用没有实际用处。

int i = 10;
std::move(i); //int转换int&&

class A {} //默认就有拷贝构造A&和移动构造A&&
A a1;
A a2(std::move(a1)); //move返回A&&会执行移动构造

左值、左值引用、常量左值引用之间的转换

#include <iostream>

class Base {
public:
	Base() = default;

	Base(Base& other) = delete;
};


int main() {
	Base b1;
	Base& refBase1 = b1; // T -> T&
	const Base& crBase1 = b1; //T -> const T&


	Base b2 = refBase1; //T& -> T,需要拷贝构造:Base(Base& other) {}
	const Base& crBase2 = refBase1; //T& -> const T&


	Base b3 = crBase1; // const T& -> T,需要拷贝构造:Base(Base& other) {}
	Base& refBase2 = crBase1; // const T& -> T&,不能直接赋值,要用const_cast
	Base& refBase2 = const_cast<Base&>(crBase1); // const T& -> T&


	Base* ptrB1 = &b1;
	Base b4 = *ptrB1; //T* -> T,需要拷贝构造:Base(Base& other) {}
}
  • 可以看出不论是const T&、T&、T*要转换到T都需要拷贝构造,也就是说会发生拷贝

  • const T&转到T&需要用const_cast


评论