C++11中的左右值引用(略带复习)

引子:我们在之前学过,auto(底层为迭代器),范围for,{}初始化(可以省略=),initializer_list,decltype,nullptr(Null字面上为0),arrray(与数组相比,对于出错的处理不同),forward_list(与普通的相比少一点空间),还学过unordered_map与unordered_set(底层为哈希表,详见我前一篇封装)新容器等内容,这些其实是C++11新增的内容,今天我们就来讲一讲另一个C++11的内容------左右值的引用!

一,初解:何为左值,何为右值?

左值(Lvalue)和右值(Rvalue)是表达式的两种分类。

左值指的是可以出现在赋值表达式的左边的表达式,它表示一个对象的身份,即内存地址。

右值则是不能出现在赋值表达式的左边的表达式,它表示一个临时的值或者一个无法获取地址的表达式。

左值引用就是给左值的引用,给左值取别名。----&

右值引用就是对右值的引用,给右值取别名----&&

为什么用&&来右值引用?

我认为:

一,通过使用右值引用,可以确保只有对象的值被移动,而不是对象的引用或指针

二,临时对象通常是右值,它们在表达式中产生并立即被使用。使用&&可以高效地利用这些临时对象,通过移动它们的资源而不是进行复制

举例:左右值:如下

cpp 复制代码
// 左值
int* p = new int(0);
int b = 1;
const int c = 2;
double x = 1.1, y = 2.2;

// 右值
10;
x + y;
fmin(x, y);

举例:左右值引用:如下

cpp 复制代码
// 左值引用
int*& rp = p;
int& rb = b;
const int& rc = c;
int& pvalue = *p;

// 右值引用
int&& rr1 = 10;
double&& rr2 = x + y;
double&& rr3 = fmin(x, y);

二,再解:左右值引用的转换。

左值引用总结: 1. 左值引用只能引用左值,不能引用右值。 2. 但是const左值引用既可引用左值,也可引用右值

右值引用总结: 1. 右值引用只能右值,不能引用左值。 2. 但是右值引用可以move以后的左值。

move是什么?

其实也不用太复杂!我认为就是一个强转!就是转成右值!

补:其实在底层上,都开了空间,但是在语法上不显示

引用转换:

cpp 复制代码
//左转右
int h ;//左值
int& d = 10;//左值引用,,错误
double& n =x+y;//左值引用,,错误
double& j = fmin(x, y);//左值引用,错误
const int& a = 10;//左值引用
const double& k = x+y;//左值引用
const double&l = fmin(x,y);//左值引用

//右转左
int g=10;
int&& t =g;//右值引用,错误
int&& t1 = move(g); //右值引用

如上图错误:

四,后解:右值引用的应用场景与作用!

主要有以下几种:

一,临时对象:例如函数返回的值,它们是右值。

二,移动语义:通过std::move将资源从一个地方移动到另一个地方,避免复制的开销。

移动构造本质是将参数右值的资源窃取过来,占位已有,那么就不 用做深拷贝了,所以它叫做移动构造,就是窃取别人的资源来构造自己

三,右值引用:允许以右值引用的方式访问右值,这在现代C++编程中用于实现资源的高效转移。

四,完美转发:在模板编程中,完美转发允许将参数以左值或右值的形式传递给其他函数,保持其值类别

我认为右值引用是为了弥补左值引用无法解决传值的问题,可以节省一个拷贝构造!

参考代码:

cpp 复制代码
class Resource {
public:
    void swap(Resource& s)
    {
        std::swap(size, s.size);
        std::swap(data, s.data);
    }

    Resource(int size) 
        : size(size)
        , data(new int[size]) 
    {
        cout << "初始构造" << endl;
    }

    // 移动构造函数
    Resource(Resource&& other) 
        : size(0)
        , data(nullptr) 
    {
       cout << "移动构造" << endl;
       swap(other);
    }

    // 拷贝构造函数
    Resource(const Resource& s)
    {
        cout << "拷贝构造函数" << endl;
        Resource cmp(s.size);
        swap(cmp);
    }

    // 移动赋值运算符
    Resource& operator=(Resource&& other)
    {
        if (this != &other) {
            cout << "移动赋值运算" << endl;
            swap(other);
        }
        return *this;
    }

    // 拷贝赋值运算
    Resource& operator=(const Resource& s)
    {
        cout << "拷贝赋值" << endl;
        Resource cmp(s.size);
        swap(cmp);
    }

    ~Resource() 
    {
        delete[] data;
    }

private:
    int* data;
    int size;
};


int main() 
{
    Resource h1(3);
    Resource h2(h1);
    Resource h3(move(h1));

    Resource h4 =move(h2);

    return 0;
}

补:其实右值引用的本质上还是左值!

五,万能的转发(补)

万能的转发是指在模板函数中使用std::forward来转发参数,它可以保持参数的原始值类别(左值或右值)。这在模板编程中非常有用,因为它允许函数模板接受不同类型的参数,并将这些参数以它们原始的值类别传递给其他函数

测试代码:

cpp 复制代码
void Fun(int& x) { cout << "左值引用" << endl; }
void Fun(const int& x) { cout << "const 左值引用" << endl; }
void Fun(int&& x) { cout << "右值引用" << endl; }
void Fun(const int&& x) { cout << "const 右值引用" << endl; }

 /*引用
 传左值->左值引用
 传右值->右值引用*/
template<typename T>
void PerfectForward(T&& t)
{
	// 模版实例化是左值引用,保持属性直接传参给Fun
	// 模版实例化是右值引用,右值引用属性会退化成左值,转换成右值属性再传参给Fun
	Fun(forward<T>(t));
}

int main()
{
	PerfectForward(10);           // 右值

	int a;
	PerfectForward(a);            // 左值

	PerfectForward(std::move(a)); // 右值

	const int b = 8;
	PerfectForward(b);			  // const 左值
	PerfectForward(std::move(b)); // const 右值

	return 0;
}

结果:

下集:我们进入可变参数的摸板分享!

相关推荐
老猿讲编程10 分钟前
一个例子来说明Ada语言的实时性支持
开发语言·ada
UestcXiye1 小时前
《TCP/IP网络编程》学习笔记 | Chapter 3:地址族与数据序列
c++·计算机网络·ip·tcp
Chrikk1 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*1 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue1 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang
考试宝1 小时前
国家宠物美容师职业技能等级评价(高级)理论考试题
经验分享·笔记·职场和发展·学习方法·业界资讯·宠物
杜杜的man1 小时前
【go从零单排】go语言中的指针
开发语言·后端·golang
霁月风2 小时前
设计模式——适配器模式
c++·适配器模式
萧鼎2 小时前
Python并发编程库:Asyncio的异步编程实战
开发语言·数据库·python·异步
学地理的小胖砸3 小时前
【一些关于Python的信息和帮助】
开发语言·python