C++11的版本在类型系统上下了很大的功夫,添加了诸如auto,decltype,move等新的关键词来简化代码的编写与降低阅读代码的难度。为了更好的理解这些新的语义,笔者确定通过几篇文章来简单窥探一下C++类型系统的冰山一角,如果加深了对C++类型系统的理解,想必大家也能够更好的应用由C++11带给我们的新"利器"。

1.左值与右值

左值(lvalue)和右值(rvalue)是C++类型系统之中的基础概念,我们不需要了解这些基础概念,同样也能写出代码。但是如果没有弄清左右值的概念,对于许多C++高级特性的探索会一叶障目,所以笔者尝试总结一下自己对于左值与右值的理解。

在C++11之前的版本,基本沿用了C语言之中对于左值与右值的定义,说起来也很简单:“在C++之中的变量只有左值与右值两种:其中凡是可以取地址的变量就是左值,而没有名字的临时变量,字面量就是右值”。 正是因为这两种变量分别位于=的左右两侧,所以被命名为左值与右值。下面,举个栗子:

int x;

int y;

x = 1;

y = 2;

x = y;

y = x;

// 以下代码有误

3 = x;

x + y = 4;

通过上述的代码我们可以快速的理解,显然x,y作为变量可以存在=的左侧,而称之为左值,而3,x + y作为字面量或中间结果,没有办法取得地址,则称之为右值。 这里笔者也给一个简单判定的左右值的方式:

判断能否取值的地址,能取地址的就是左值。

2.将亡值

其实上一节对于左值右值的描述,在我们编写绝大多数代码的场景下并没有什么影响。而在C++11扩展了右值的的概念,将右值分为了纯右值(pure rvalue)与将亡值(eXpiring Value)。纯右值的概念等同于我们之前所理解的右值,指的是临时变量或字面量值;而将亡值是C++11新引入的概念,它依托于右值。

在C++之中,使用左值去初始化对象或为对象赋值时,会调用拷贝构造函数或赋值构造函数。而使用一个右值来初始化或赋值时,会调用移动构造函数或移动赋值运算符来移动资源,从而避免拷贝,提高效率。 而将亡值可以理解为通过移动构造其他变量内存空间的方式获取到的值。在确保其他变量不再被使用、或即将被销毁时,来延长变量值的生命期。而实际上该右值会马上被销毁,所以称之为:将亡值。

上述概念阐述的略微抽象,我们来看下面这段代码:

这是我们简单定义的Time类,在类中我们定义了拷贝构造函数和移动构造函数:

class Time {

public:

int* hour;

int* minute;

int* second;

Time(int h, int m, int s) {

hour = new int(h);

minute = new int(m);

second = new int(s);

}

Time(const Time& t) {

cout << "copy" << endl;

hour = new int(*t.hour);

minute = new int(*t.minute);

second = new int(*t.second);

}

Time(Time&& t) noexcept:hour(t.hour),minute(t.minute),second(t.second) {

t.hour = nullptr;

t.minute = nullptr;

t.second = nullptr;

cout << "move" << endl;

}

~Time() {

cout << "call ~Time()" << endl;

delete hour;

delete minute;

delete second;

}

};

接下来我们执行下面的代码:

int main()

{

Time test(10,25,12);

Time test2(test);

return 0;

}

执行结果:

copy

call ~Time()

call ~Time()

由上述代码我们看到test2对象调用了拷贝构造函数来构造了新的对象,这个过程显然是更占用内存的。而接下来,我们尝试利用move函数将test强行转化为将亡值,来避免内存重新分配的过程。但是之后我们也无法再访问test对象的内容了,因为都在移动构造函数之中置为了空指针。

int main()

{

Time test(10,25,12);

Time test2(move(test));

return 0;

}

执行结果:

move

call ~Time()

call ~Time()

通过这样的方式来减少不必要的内存操作。但是之后我们也无法再访问test对象的内容了,因为都在移动构造函数之中置为了空指针。将亡值通过移动构造函数”借尸还魂“,通过test2变量延续了自己的生命周期。

3.左值的一些"坑"

虽然笔者给出了左右值分辨的一些基本标准,但是还是有下面一些很让人迷惑的场景:

条件表达式返回左值true ? i : i;

++i++ // 左值

++i // 右值

[]数组取值返回左值i[10]

指针取值操作符返回左值*i

字符串字面量返回左值“hello world”

这是一些表示左值的特殊情况,这里笔者也不展开一一赘述了,希望大家可以简单的进行记忆。当然,笔者从来不去记一些太琐碎的问题,因为太他喵难记了,所以在C++11之中,可以标准库中添加的模板类is_lvalue_reference来判断表达式是否为左值,is_rvalue_reference来判断是否为右值。

cout << is_lvalue_reference::value << endl;

cout << is_rvalue_reference::value << endl;

返回1则为真,0为假。

4.小结

这只是我们对C++类型系统的第一篇探讨,后续笔者还会继续深入的探讨有关C++11新特性之中与类型系统相关的内容,欢迎大家多多讨论,指教。

内容来源于网络如有侵权请私信删除

c语言 变量的左值和右值,C++雾中风景10:聊聊左值,纯右值与将亡值相关推荐

  1. c++ 左值 广义左值 右值 纯右值 将亡值

    为什么C/C++等少数编程语言要区分左右值? 历史原因: C语言作为一门古老的编程语言,其设计初衷是为了在硬件资源有限的系统上进行高效的编程,因此其语法和语义设计相对较简单.左值和右值的概念最初是由C ...

  2. 左值右值将亡值泛左值

    左右值概念 简单理解 左值:赋值运算符左边的变量,可以接受右边值,例如 int a = 10; a就是一个左值右值:赋值运算符右边的值,这个值可以是一个变量也可以是一个常量,例如 int a = 10 ...

  3. 左值、右值、将亡值 | 左值引用、右值引用

    左值.右值.将亡值 左值.右值 左值(lvalue)中l指的是location,是有明确地址的,可寻址的值.右值(rvalue)中r指的是read,是可读的值,不一定可以寻址.左值可以当右值使用,但右 ...

  4. 存储过程debug值not a variable_C语言变量的存储类别

    在程序中经常会使用到变量,在C程序中可以选择变量的不同存储形式,其存储类别分为静态存储和动态存储.可以通过存储类别修饰符来告诉编译器要处理什么样的类型变量,具体主要有自动(auto).静态(stati ...

  5. C语言中的关键字,变量的定义,变量的命名规则,交换两个变量的值,驼峰命名法【 C语言变量名命名法则】

    C语言结构 C语言中的关键字 变量 变量的定义 变量的命名规则 交换两个变量的值 驼峰命名法 C语言结构 上图中我们可以看到最外层是程序,内部是所有的构成,我们从最里面开始说明. 当我们用计算机语言来 ...

  6. 【C++grammar】左值、右值和将亡值

    目录 C++03的左值和右值 C++11的左值和右值 将亡值 在C++03中就有相关的概念 C++03的左值和右值 通俗的理解: (1) 能放在等号左边的是lvalue (2) 只能放在等号右边的是r ...

  7. Cpp / 右值、纯右值、将亡值

    一.左值与右值 左值(lvalue)和右值(rvalue)是 C++ 类型系统之中的基础概念,我们不需要了解这些基础概念,同样也能写出代码.但是如果没有弄清左右值的概念,对于许多 C++ 高级特性的探 ...

  8. c ++一行给多个变量赋值_C语言变量

    变量是程序可操作的存储区的名称. 变量其实只不过是程序可操作的存储区的名称.C 中每个变量都有特定的类型,类型决定了变量存储的大小和布局,该范围内的值都可以存储在内存中,运算符可应用于变量上. 变量的 ...

  9. 非零基础自学Golang 第3章 基本变量与类型 3.1 Go语言变量

    非零基础自学Golang 文章目录 非零基础自学Golang 第3章 基本变量与类型 3.1 Go语言变量 3.1.1 声明变量 3.1.2 初始化变量 3.1.3 变量值交换 3.1.4 匿名变量 ...

最新文章

  1. SAD和SATD的区别[摘]
  2. 使用云祺虚拟机备份软件恢复Citrix XenServer 虚拟机
  3. Docker的安装和镜像管理并利用Docker容器实现nginx的负载均衡、动静分离
  4. WordPress.com 开源,弃 PHP 改用 JavaScript
  5. 算法导论-排序算法-分治法
  6. 一次排查服务器端接口报500错误的经历
  7. 2012怎么设置index.php,配置伪静态.htaccess去掉wordpress固定连接里的index.php
  8. 美团王兴怒卸百度 App
  9. PLSQL使用text import导入数据
  10. epson连接计算机后无法打印,如何解决连接到Epson打印机后计算机无法打印的问题...
  11. Alt键一直处于按下状态 解决办法
  12. element ui 表格头部内容不换行
  13. 015:苹果和虫子2
  14. jquery实现歌词滚动
  15. ipad协议,接口稳定版
  16. 手机解除移动宽带屏蔽_家用宽带为什么Wifi比有线网速快很多?是谁偷走了你的带宽?...
  17. 关于通信方面的总结(通信协议、通信端口)
  18. HTML协议目标端口和源端口,协议:TCP源IP:源端口:80目的IP:目的端口:4049TT? 爱问知识人...
  19. Part6:客户端和服务端信息交互模型
  20. PHP获取谷歌邮箱的邮件附件imap

热门文章

  1. teraterm 执行sql命令_tera term的ttl脚本使用方法 | 学步园
  2. C语言入门题-7-1 最大和最小 (10分)
  3. linux动态路由rip配置,配置 动态路由协议OSPF和RIPv2实现全网互通^
  4. python数据写入到excel不同sheet_Python3 pandas库 (32) 将数据写入Excel多个sheet
  5. java数据接口之链表_Java数据结构和算法之链表
  6. php随机数字不重复使等式成立_当随机数遇上量子
  7. CentOS 编译安装 Nodejs (实测 笔记 Centos 7.3 + node 6.9.5)
  8. Ant Design Pro+Electron+electron-builder实现React应用脱离浏览器,桌面安装运行
  9. bzoj1110: [POI2007]砝码Odw
  10. Python 模块之 time datetime