C++--右值和移动语义

目录

一,引言

二,左值与右值

三,左值引用和右值引用

1,生命周期

2,参数匹配问题

四,移动构造和移动赋值

五,总结


一,引言

在C++11之前没有引出右值的概念,导致一些问题无法得到全面解决,例如如下问题:

cpp 复制代码
string addStrings(string num1, string num2) {
    string str;
    int end1 = num1.size() - 1, end2 = num2.size() - 1;
    // 进位

        int next = 0;
    while (end1 >= 0 || end2 >= 0)
    {
        int val1 = end1 >= 0 ? num1[end1--] - '0' : 0;
        int val2 = end2 >= 0 ? num2[end2--] - '0' : 0;
        int ret = val1 + val2 + next;
        next = ret / 10;
        ret = ret % 10;
        str += ('0' + ret);
    }
    if (next == 1)
        str += '1';
    reverse(str.begin(), str.end());
    return str;
}

如上述问题,返回时不能使用string& ,因为str为一个局部对象。而频繁的拷贝构造造成更多额外的空间消耗。为此引入右值移动语义


二,左值与右值

通常来说能过取地址的为左值,不能取地址的为右值。通常来说右值一般是临时对象,临时变量,常量等等,举一些例子如下:

cpp 复制代码
int* p = new int(0);
int b = 1;
const int c = b;
*p = 10;
string s("111111");
s[0] = 'x';

上述都可以进行取地址,所以被称为左值。

cpp 复制代码
10;
x + y;
fmin(x, y);
string("11111");

上述都不可以进行取地址,所以被称为右值。第三个函数返回的临时变量,以及最后一个返回的临时对象。


三,左值引用和右值引用

左值引用,在以前的学习中几乎所有的引用都是左值引用。右值引用的格式如下:

语法设计上:

1,左值引用不能直接引用右值引用,但是const 左值引用可以引用右值引用。如下:

2,右值引用不能直接引用左值,但是右值引用可以引用move(左值)。如下:

3,变量表达式的属性都是左值,也就意味着右值引用变量的属性也是左值。如下:

变量b本身的属性是左值。所有可以被a所引用。


1,生命周期

右值引用可以延长生命周期,上文中讲到右值一般是临时对象等等。因此一般来说右值的生命周期只在本行。但是通过右值引用可以延长右值的生命周期,延长到右值引用变量的生命周期。该变量销毁,该右值的生命周期结束。如下:

cpp 复制代码
 std::string s1 = "Test";
 //  std::string&& r1 = s1;           
 const std::string& r2 = s1 + s1;
 //  r2 += "Test";                    
 std::string&& r3 = s1 + s1;

r3延长了s1+s1结果的生命周期。


2,参数匹配问题

在之前写的函数中,一般参数类型都是const 左值。在传参的过程中,不管是左值还是右值都可以进行接收。那么在C++11新语法下,右值究竟是如何进行匹配的。如下:

cpp 复制代码
void f(int& x)
{
    std::cout << "左值引⽤重载f(" << x << ")\n";
}
void f(const int& x)
{
    std::cout << " 到const的左值引⽤重载f(" << x << ")\n";
}
void f(int&& x)
{
    std::cout << "右值引⽤重载f(" << x << ")\n";
}
cpp 复制代码
int&& x = 1;f(x);            // 调⽤f(int& x)
f(std::move(x)); // 调⽤f(int&& x)

最后两组函数调用更加验证了右值引用表达式本质上是左值。


四,移动构造和移动赋值

在上述引入C++11右值的概念之后就引入了移动构造和移动赋值的概念。如开头的代码,当函数进行如下调用:

cpp 复制代码
cao::string ret = cao::addStrings("11111", "2222");
cout << ret.c_str() << endl;

编译器不进行构造优化的情况之下:会出现如图以下情况:

首先"11111"和"2222"会进行构造 出一个临时对象 。这个临时对象 再进行拷贝构造 给addstrings的两个形参。之后是str的构造。str拷贝构造一个临时对象。str析构。临时对象最终拷贝构造给ret。

若类型参数需要很大的空间,则频繁的拷贝构造造成大量空间的消耗。为此引出移动构造。

编译器不进行构造优化有移动构造的情况如下

情况和上述一致,之不过将拷贝构造换成了移动构造。而移动构造相当于是一种内存的抢夺,所造成空间消耗很小。为此在这种情况下,移动构造就很有优势。


移动赋值的逻辑和移动构造一致。


五,总结

左值引用和右值引用的目的都是为了减少拷贝,提高效率,其次左值引用还可以修改参数或者返回值。但是在一些情况下并不能返回左值引用。为此想要解决这个问题要么使用输出型参数,要么编译器进行优化,最后就是移动语义的作用。移动语义的作用主要在深拷贝的情况之下。浅拷贝就不需要实现移动语义。

相关推荐
非凡ghost1 分钟前
MPC-QT视频播放器(基于Qt框架播放器)
开发语言·windows·qt·音视频·软件需求
转基因3 分钟前
C++的IO流
开发语言·c++
一碗绿豆汤5 分钟前
Java语言概述和开发环境-1
java·开发语言
愈努力俞幸运9 分钟前
rust安装
开发语言·后端·rust
天天进步201532 分钟前
【Nanobrowser源码分析4】交互篇: 从指令到动作:模拟点击、滚动与输入的底层实现
开发语言·javascript·ecmascript
console.log('npc')38 分钟前
vue2中子组件父组件的修改参数
开发语言·前端·javascript
码点39 分钟前
【无标题】日文字库Japan.ini
开发语言
IT=>小脑虎43 分钟前
2026版 Python零基础小白学习知识点【基础版详解】
开发语言·python·学习
wjs20241 小时前
抽象工厂模式
开发语言
lly2024061 小时前
SVG 模糊效果详解
开发语言