C++雾中风景10:聊聊左值,纯右值与将亡值

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

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<decltype(i[10])>::value << endl;
  cout << is_rvalue_reference<decltype(i[10])>::value << endl;

返回1则为真,0为假。

4.小结

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

目录
相关文章
|
7月前
|
算法 编译器 程序员
【C/C++ 解惑 】 std::move 将左值转换为右值的背后发生了什么?
【C/C++ 解惑 】 std::move 将左值转换为右值的背后发生了什么?
71 0
|
3月前
|
编译器 C++
C++ 11新特性之右值引用
C++ 11新特性之右值引用
50 1
|
7月前
|
编译器 C语言 C++
从C语言到C++_33(C++11_上)initializer_list+右值引用+完美转发+移动构造/赋值(中)
从C语言到C++_33(C++11_上)initializer_list+右值引用+完美转发+移动构造/赋值
39 1
从C语言到C++_33(C++11_上)initializer_list+右值引用+完美转发+移动构造/赋值(中)
|
7月前
|
存储 安全 C语言
从C语言到C++_33(C++11_上)initializer_list+右值引用+完美转发+移动构造/赋值(上)
从C语言到C++_33(C++11_上)initializer_list+右值引用+完美转发+移动构造/赋值
34 2
|
7月前
|
编译器 C语言 C++
从C语言到C++_33(C++11_上)initializer_list+右值引用+完美转发+移动构造/赋值(下)
从C语言到C++_33(C++11_上)initializer_list+右值引用+完美转发+移动构造/赋值
40 1
|
6月前
|
编译器 C++ 开发者
C++一分钟之-右值引用与完美转发
【6月更文挑战第25天】C++11引入的右值引用和完美转发增强了资源管理和模板灵活性。右值引用(`&&`)用于绑定临时对象,支持移动语义,减少拷贝。移动构造和赋值允许有效“窃取”资源。完美转发通过`std::forward`保持参数原样传递,适用于通用模板。常见问题包括误解右值引用只能绑定临时对象,误用`std::forward`,忽视`noexcept`和过度使用`std::move`。高效技巧涉及利用右值引用优化容器操作,使用完美转发构造函数和创建通用工厂函数。掌握这些特性能提升代码效率和泛型编程能力。
51 0
|
7月前
|
编译器 C++ 容器
【C++11(一)】右值引用以及列表初始化
【C++11(一)】右值引用以及列表初始化
|
7月前
|
存储 安全 程序员
C++11:右值引用
C++11:右值引用
46 0
|
7月前
|
存储 算法 程序员
【C++入门到精通】右值引用 | 完美转发 C++11 [ C++入门 ]
【C++入门到精通】右值引用 | 完美转发 C++11 [ C++入门 ]
56 0
|
7月前
|
存储 人工智能 编译器
【重学C++】【引用】一文看懂引用的本质与右值引用存在的意义
【重学C++】【引用】一文看懂引用的本质与右值引用存在的意义
144 0