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。

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

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

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


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


五,总结

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

相关推荐
房开民8 分钟前
c++总结
java·开发语言·c++
好大哥呀17 分钟前
C++ 多态
java·jvm·c++
毕设源码-赖学姐22 分钟前
【开题答辩全过程】以 基于Java的医院器材管理系统的设计与实现为例,包含答辩的问题和答案
java·开发语言
float_com31 分钟前
【java常用API】----- Arrays
java·开发语言
不会写DN1 小时前
PHP 中的文件读写与上传
android·开发语言·php
LuckyTHP2 小时前
迁移shibboleth java获取shibboleth用户信息
java·开发语言
墨韵流芳2 小时前
CCF-CSP第41次认证第三题——进程通信
c++·人工智能·算法·机器学习·csp·ccf
客卿1232 小时前
数论===质数统计(暴力法,)
java·开发语言
hz_zhangrl2 小时前
CCF-GESP 等级考试 2026年3月认证C++五级真题解析
c++·青少年编程·程序设计·gesp·c++五级·gesp2026年3月·gesp c++五级
Σίσυφος19002 小时前
C++ 多肽经典面试题
开发语言·c++·面试