IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 左值与右值 -> 正文阅读

[C++知识库]左值与右值

左值与右值

一、左值
左值表示一个占据内存中可识别位置的一个对象,更进一步地,可以对左值取地址

int a = 10;
int *p = &a;
int **q = &p;

a,p,q都是很经典的左值,可以通过标识符a,p,q,取出内存地址中对应的对象

int a;// ①
a = 4;// ②

①如果在函数中执行该语句的话,变量a会在栈帧中开辟一个4字节的内存空间其值未定义。所以a为左值,能够取其地址
②赋值语句中左操作数必须是一个左值,赋值操作本质上是对内存进行更新,所以我们必须要找到内存地址才能更新
二、右值
判断右值的一个简单方法就是能不能对变量或者表达式取地址,如果不能,他就是右值

int foo{return 10;};
x+1;

函数返回的对象(非引用,非指针)是一种典型的右值,这些对象在函数返回之后会被立刻销毁,也就不存在说取地址这样的操作了。如foo() = 20是一个典型的错误
加减乘除等表达式也是右值,因为x+1的结果保存在临时寄存器中,并不会输出到内存,因为没有办法对这个结果取地址,因为它们也是左值

a+1 = 4;
foo() = 10;

a+1以及foo()返回的值均为右值,它们都是一个临时的值,在表达式结束时,生命周期结束

三、左值和右值的转换
1.左值和右值在不同的表达式中有不同的定义

int a = 10,b = 20;
int c = a + b;

在第一行中,a,b都是左值,但在第二行中,由于要执行加法,所以会将左值隐式地转换成右值,其结果也是一个右值,保存在临时寄存器中,而后写入c的内存中
2.解引用操作符*(作用于右值,返回左值)
解引用操作符作用于指针,取出指针指向的内存内容,该结果可以作为左值使用

int *p = &a;
*(p+1) = 20;

p+1的结果是一个右值,但是*(p+1)的结果是左值
3.取地址操作符&(作用于左值,返回右值)
取地址操作符& 一定是作用在左值上,这也是前面定义左值使用的方式

int a[] = {1,2,3};
int *p = &a[2];
int *q = &(a+1);//error 

四丶左值引用
引用类型又称之为”左值引用“,顾名思义,引用只能引用一个左值,而不能是右值

string& s = string("hello");//error,这里引用了右值

但常量引用可以引用一个右值

const string& s = string("hello");
void foo(string& s){};
foo("hello"); //error 一个右值无法转换成左值引用
void foo1(const string& s){};
foo1("hello"); //一个右值可以转换成常量引用
int a = 10;
const int b = 20;

const int& c = a;//引用非常量右值
const int& d = b;//引用常量左值
const int& e = 10+20 //引用右值(正常来说,10+20这一右值会在表示结束时被销毁,但是常量引用演唱了其生命周期)

五、右值引用
右值引用是C++11的新特性,主要解决”移动语义“,以及”完美转发“,右值引用的标志是&&,顾名思义,右值引用专门为右值而生,可以指向右值,不能指向左值

int &&a = 5;
int b = 5;
int &&aa = b;//error,右值引用不可以指向左值

a = 6;//右值引用的用途,可以修改右值

右值引用本身是一个左值,这句话听起来有点难理解,但是右值引用是一个变量,而变量一般都是左值

int && s = 1;
int && q = s; //error 此时s为左值

右值引用由于引用的是右值,而右值又是临时的,随时可能被销毁的对象。因此在使用右值引用的地方,我们可以随意接管所引用对象的资源,而不用担心内存泄漏,数据被更改等情况

六丶移动语义
在一般的拷贝赋值函数中,我们通常会使用临时对象+swap的方式来实现异常安全以及数据安全

class Buz {
private:
    int* m_ptr;
public:
    Buz(int *ptr = 0):m_ptr(ptr){}
    ~Buz() { delete m_ptr; }
    Buz(const Buz& other);
    Buz& operator=(const Buz& rhs) {
        Buz temp = Buz(rhs);
        swap(this->m_ptr, temp.m_ptr);
        return *this;
    }
};

在上述代码中,首先使用rhs通过拷贝构造函数构造出了临时对象temp,然后交换this和temp中的数据。当函数返回时,temp对象将被自动调用其析构函数并销毁,从而释放掉原来this的数据。这样的写法是异常安全的

在buz = Buz();这一赋值后面发生了很多事,首先创建Buz对象,然后将这个临时对象赋值给buz,此时将会调用拷贝赋值,而在拷贝赋值又会生成一个临时对象,如此一来,Buz对象相当于被创建了两次

在使用了右值引用以后,我们就可以使用移动的方式来编写移动赋值函数,直接将右值中的数据搬到自己这边来

Buz& operator = (Buz&& rhs) {
    swap(this->ptr, rhs.ptr);
    return *this;
}

buz = Buz();中的Buz()是一个右值,所以该赋值语句将调用移动赋值函数

移动语义利用的基本事实就是右值本身就是临时的,随时可能被销毁的,那么我们从右值中“窃取”数据就不会有任何副作用。当我们窃取完数据以后,甚至可以将一些我们需要销毁的数据挂在右值上,右值销毁时带着这些数据一并销毁

我们可以通过move()函数将一个左值强制类型转成一个右值引用,注意move()本身不具有移动语义,它只是一个类型转换函数而已,内部可以通过static_cast实现

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-09-19 07:48:45  更:2021-09-19 07:50:03 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 22:57:15-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码