C++新特性——右值引用

1、What

        C++11引入的一种新型引用,用于提高程序的性能和资源管理的效率。它通过&&符号来表示,只能绑定到右值(包括将亡值,即临时对象)上,不能绑定到左值上。

1.1 左值和右值的区别

        左值是一个表示数据的具名表达式,可以获取它的地址并且对其赋值,可出现在赋值操作符=的左边,具有一段较长的生命周期。右值是一个表达数据的匿名表达式,比如字面常量、表达式返回值、函数返回值等,其生命周期很短,可出现在赋值操作符=的右边,但是不能出现在=的左边,右值不能取地址

1、整型x的x++是右值,编译器首先生成x值的临时复制,然后对x递增,最后返回临时复制内容。++x则是左值,对x递增后马上返回其自身。

2、通常字面量是右值,但字符串字面量除外。

1.2 补充概念:泛左值、将亡值、纯右值

泛左值:通过评估能够确定对象、位域或函数的标识的表达式
纯右值:通过评估能够用于初始化对象和位域,或者能够计算运算符操作数的值的表达式
将亡值:属于泛左值的一种,表示资源可以被重用的对象和位域,它们接近其生命周期的末尾;也可能经过右值引用的转换产生。

2、Why

        为了解决C++中资源管理的效率和性能问题。对象的复制和移动是常见的操作,当对象包含大量数据时,复制操作会导致性能开销。右值引用允许转移对象的资源(如动态分配的内存、文件句柄等),而不是通过复制来共享资源,可以显著减少不必要的拷贝和内存分配,提高程序的运行效率。

3、When

  1. 移动语义:将资源(如动态内存、文件句柄等)从一个对象“移动”到另一个对象,而不是复制它们。如在将大型对象(如std::vectorstd::string等)赋值给另一个对象时,使用右值引用可以显著提高性能。

  2. 完美转发:在模板编程中保持参数的原始类型(左值或右值),以便在转发给另一个函数时保持其行为(例如,是应该进行拷贝还是移动)。这可以避免在函数调用过程中丢失参数类型信息的问题,从而提高代码的可维护性和灵活性。

4、How

1、声明右值引用

在类型名称后面加上&&符号。如:
int&& rref = 10; // rref 是一个右值引用,绑定到整数 10 这个右值上

2、作为函数参数

如果函数的参数是右值引用,也需要在类型名称后面加上&&符号。如:
void foo(int&& x) { // 函数体内可以使用 x,它是对传入右值的引用  
    // 函数体  
}

3、实现移动构造函数和移动赋值运算符

定义移动构造函数和移动赋值运算符,允许资源从源对象直接“移动”到目标对象,而不是通过复制来共享资源。如:

class String {  
public:  
    String(const char* str = "") : _str(new char[strlen(str) + 1]) {  
        strcpy(_str, str);  
    }  
    // 移动构造函数  
    String(String&& other) noexcept : _str(other._str) {  
        other._str = nullptr; // 将源对象的资源转移给目标对象,并清空源对象  
    }  
    // 移动赋值运算符  
    String& operator=(String&& other) noexcept {  
        if (this != &other) {  
            delete[] _str;  
            _str = other._str;  
            other._str = nullptr;  
        }  
        return *this;  
    }  
    ~String() {  
        delete[] _str;  
    }  
    // 禁止拷贝构造和拷贝赋值  
    String(const String&) = delete;  
    String& operator=(const String&) = delete;  
    void print() const {  
        std::cout << _str << std::endl;  
    }  
private:  
    char* _str;  
};

4、使用std::move

std::move是一个函数模板,它的主要作用是将一个对象转换为右值引用,以便可以对其进行移动操作。但需要注意的是,std::move本身并不移动任何东西,它只是将对象转换为右值引用,让编译器知道该对象可以被移动。如:
String s1("Hello");  
String s2 = std::move(s1); // 使用 std::move 将 s1 的资源移动给 s2
在这个例子中,我们使用std::move将s1转换为右值引用,并将其资源移动给s2。

5、使用std::forward(完美转发)

std::forward 是一个函数模板,其保持参数的引用性质(左值引用或右值引用)。

通用引用(也称为转发引用)T&&,可以绑定到左值或右值。

#include <iostream>
#include <utility>

class Wrapper {
public:
    Wrapper(int& value) : value_(value) {
        std::cout << "Constructed with lvalue reference\n";
    }
    Wrapper(int&& value) : value_(value) {
        std::cout << "Constructed with rvalue reference\n";
    }
    ~Wrapper() {
        // Do nothing in this simple example
    }
    void print() const {
        std::cout << "Value: " << value_ << std::endl;
    }
private:
    int& value_;
};

template<typename T>
void forwardWrapper(T&& t) {
    Wrapper w(std::forward<T>(t));
    w.print();
}

int main() {
    int x = 42;
    // 传递左值
    forwardWrapper(x);
    // 传递右值
    forwardWrapper(42);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gaopeng@step by step

你我共同进步

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值