左值与右值,以及c++11的相关特性。

news2025/1/16 18:58:44

目录

左值

右值

左值引用总结:

右值引用总结:

右值引用使用场景和意义:

        1、左值引用的使用场景:

编译器优化1:

2、移动构造与移动赋值:

3、右值引用的使用场景: 

        编译器优化2:

4、右值引用引用左值及其一些更深入的使用场景分析

5、完美转发

总结:  

 新的类功能

(新增加的两个函数):

成员变量初始缺省值        

强制生成默认函数的关键字default:       

        禁止生成默认函数的关键字delete

 继承和多态中的final与override关键字


左值

        是一个表示数据的表达式(如变量名或解引用的指针),我们可以获取它的地址+可以对它赋 值左值可以出现赋值符号的左边,右值不能出现在赋值符号左边。定义时const修饰符后的左 值,不能给他赋值,但是可以取它的地址。左值引用就是给左值的引用,给左值取别名。

右值

        也是一个表示数据的表达式,如:字面常量、表达式返回值,函数返回值(这个不能是左值引 用返回)等等,右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,右值不能 取地址。右值引用就是对右值的引用,给右值取别名。 

        右值又分为:内置类型称为纯右值:一个数字或一个字面值或一个常量,它并不标识任何位置。自定义类型称为将亡值。

举例:

int a=1;
double b=0.32;
//自定义的类型,也是左值
//下式中的右边全是右值,右值又分为:内置类型称为纯右值:一个数字或一个字面值或一个常量,它并不标识任何位置。
//自定义类型称为将亡值。
int c=a+b;
string str="adffadsf";
//函数返回值也是右值
int add(int a,int b){
    int c=a+b;
    return c;
}
//返回c时,将c拷贝给一个临时变量,这个临时变量就是右值。

左值引用总结:

1. 左值引用只能引用左值,不能引用右值。

2. 但是const左值引用既可引用左值,也可引用右值。

int main()
 {
 // 左值引用只能引用左值,不能引用右值。
 int a = 10;
 int& ra1 = a;   
// ra为a的别名
//int& ra2 = 10;   // 编译失败,因为10是右值
// const左值引用既可引用左值,也可引用右值。
const int& ra3 = 10;
 const int& ra4 = a;
 return 0;
 }

右值引用总结:

1. 右值引用只能右值,不能引用左值。

2. 但是右值引用可以move以后的左值。

int main()
 {
 // 右值引用只能右值,不能引用左值。
int&& r1 = 10;
 // error C2440: “初始化”: 无法从“int”转换为“int &&”
 // message : 无法将左值绑定到右值引用
int a = 10;
 int&& r2 = a;
 // 右值引用可以引用move以后的左值
int&& r3 = std::move(a);
 return 0;
 }

细节:对于常量的右值引用不是取别名,而是生成对象,由地址可以看出 。

对于数字常量不可取地址。

右值引用使用场景和意义:

        1、左值引用的使用场景:

        做参数和做返回值都可以提高效率。

void func1(bit::string s)
 {}
 void func2(const bit::string& s)
 {}
 int main()
 {
 bit::string s1("hello world");
 // func1和func2的调用我们可以看到左值引用做参数减少了拷贝,提高效率的使用场景和价值
func1(s1);
 func2(s1);
 }
 // string operator+=(char ch)  传值返回存在深拷贝
// string& operator+=(char ch) 传左值引用没有拷贝提高了效率
s1 += '!';
 return 0;
}

编译器优化1

一般编译器会优化省略返回值到中间变量这次拷贝

2、移动构造与移动赋值:

// 拷贝构造
string(const string& s) : _str(nullptr) {
    cout << "string(const string& s) -- 深拷贝" << endl;
    string tmp(s._str);
    swap(tmp);
}
// 赋值重载
string& operator=(const string& s) {
    cout << "string& operator=(string s) -- 深拷贝" << endl;
    string tmp(s);
    swap(tmp);
    return *this;
}
// 移动构造
string(string&& s) : _str(nullptr), _size(0), _capacity(0) {
    cout << "string(string&& s) -- 移动语义" << endl;
    swap(s);
}
// 移动赋值
string& operator=(string&& s) {
    cout << "string& operator=(string&& s) -- 移动语义" << endl;
    swap(s);
    return *this;
}

        上面的代码可知:采用右值引用作为形参接受实参的构造函数赋值运算符重载被称为移动构造移动赋值。

  • 当构造新对象和赋值对象时,用右值(自定义类型是将亡值)进行时,会调用移动构造和移动赋值。可以发现本质是当前构造或赋值对象与右值进行交换,这个交换会交换两值在栈区的值,右值在堆区上的内容没有改变,只是指向的指针变成了构造或赋值的对象,综上,当这条构造或赋值语句结束后,拷贝的空间仅有栈区上的指针和其他信息,也就是浅拷贝实现了深拷贝的功能,右值生命周期结束时,销毁的是构造或者赋值对象的空间。
  • 如果拷贝构造和普通的赋值运算符重载的话,会进行深拷贝,将堆区上的内容也要拷贝一次,
  • 由此可见,尤其是对象比较大时(堆区上存放实际信息),移动构造和移动赋值极大的减少了时间复杂度。

3、右值引用的使用场景: 

namespace bit {
bit::string to_string(int value) {
    bool flag = true;
    if (value < 0) {
        flag = false;
        value = 0 - value;
    }
    bit::string str;
    while (value > 0) {
        int x = value % 10;
        value /= 10;
        str += ('0' + x);
    }
    if (flag == false) {
        str += '-';
    }
}
} // namespace bit

            但是当函数返回对象是一个局部变量,出了函数作用域就不存在了,就不能使用左值引用返回, 只能传值返回。例如:bit::string to_string(int value)函数中可以看到,这里只能使用传值返回, 传值返回会导致至少1次拷贝构造(如果是一些旧一点的编译器可能是两次拷贝构造,)

        编译器优化2:

虽然返回值str是局部变量为左值,因为这种应用场景十分常见,所以编译器会将str识别成右值,实现右值引用,调用移动拷贝和移动赋值.

         右值引用和移动语义解决上述问题: 在bit::string中增加移动构造,移动构造本质是将参数右值的资源窃取过来,占位已有,那么就不用做深拷贝了,所以它叫做移动构造,就是窃取别人的资源来构造自己。

 

4、右值引用引用左值及其一些更深入的使用场景分析

按照语法,右值引用只能引用右值,但右值引用一定不能引用左值吗?因为:有些场景下,可能 真的需要用右值去引用左值实现移动语义。当需要用右值引用引用一个左值时,可以通过move 函数将左值转化为右值。C++11中,std::move()函数位于 头文件中,该函数名字具有迷惑性, 它并不搬移任何东西,唯一的功能就是将一个左值强制转化为右值引用,然后实现移动语义。

5、完美转发

  1. 模板中的&&不代表右值引用,而是万能引用,其既能接收左值又能接收右值。
  2. template<typename T>
     void PerfectForward(T&& t)
     {
     Fun(t);
     }
  3. 模板的万能引用只是提供了能够接收同时接收左值引用和右值引用的能力,
  4. 引用类型的唯一作用就是限制了接收的类型,后续使用中都退化成了左值,
  5. 我们希望能够在传递过程中保持它的左值或者右值的属性, 就需要用我们下面学习的完美转发
​​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; }
// std::forward<T>(t)在传参的过程中保持了t的原生类型属性。
template<typename T>
void PerfectForward(T&& t)
{
	Fun(std::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;
}

    

总结:  

        c++11增加右值引用这一语法,实现移动拷贝和移动赋值,从而避免了很多场景下的将亡值(右值)在堆区空间资源的拷贝,对右值的堆区资源的掠夺,优化了时间复杂度。STL容器插入接口函数也增加了右值引用版本,并且采用了完美转发,避免右值退化成左值。

 新的类功能

(新增加的两个函数):

原来C++类中,有6个默认成员函数:

1. 构造函数

2. 析构函数

3. 拷贝构造函数

4. 拷贝赋值重载

5. 取地址重载

6. const 取地址重载

最后重要的是前4个,后两个用处不大。默认成员函数就是我们不写编译器会生成一个默认的。


         针对移动构造函数和移动赋值运算符重载有一些需要注意的点如下:

        如果你没有自己实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个。那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造, 如果实现了就调用移动构造,没有实现就调用拷贝构造。

        如果你没有自己实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个,那么编译器会自动生成一个默认移动赋值。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动赋值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造 完全类似) 如果你提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值。

成员变量初始缺省值        

 类成员变量初始化 C++11允许在类定义时给成员变量初始缺省值,默认生成构造函数会使用这些缺省值初始化,这个我们在雷和对象默认就讲了,这里就不再细讲了。

强制生成默认函数的关键字default:       

         强制生成默认函数的关键字default: C++11可以让你更好的控制要使用的默认函数。假设你要使用某个默认的函数,但是因为一些原因这个函数没有默认生成。比如:我们提供了拷贝构造,就不会生成移动构造了,那么我们可以 使用default关键字显示指定移动构造生成

class Person {
public:
    Person(const char* name = "", int age = 0) : _name(name), _age(age) {}
    Person(const Person& p) : _name(p._name), _age(p._age) {}
    Person(Person&& p) = default;

private:
    bit::string _name;
    int _age;
};
int main() {
    Person s1;
    Person s2 = s1;
    Person s3 = std::move(s1);
    return 0;
}

        禁止生成默认函数的关键字delete

        如果能想要限制某些默认函数的生成,在C++98中,是该函数设置成private,并且只声明补丁 已,这样只要其他人想要调用就会报错。在C++11中更简单,只需在该函数声明加上=delete即 可,该语法指示编译器不生成对应函数的默认版本,称=delete修饰的函数为删除函数。

class Person {
public:
    Person(const char* name = "", int age = 0) : _name(name), _age(age) {}
    Person(const Person& p) = delete;

private:
    bit::string _name;
    int _age;
};
int main() {
    Person s1;
    Person s2 = s1;
    Person s3 = std::move(s1);
}
return 0;

 继承和多态中的final与override关键字

这个我们在继承和多态章节已经进行了详细讲解继承和多态

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1565079.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Qt_Note20_QML_自定义Grid控件与OpacityMask的使用

import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 2.12 import QtGraphicalEffects 1.14Window {visible: truewidth: 640height: 480title: qsTr("Hello World")// 自定义Grid控件与OpacityMask的使用Grid {id: gridwidth: 15height: 200co…

SQL语句学习+牛客基础39SQL

什么是SQL&#xff1f; SQL (Structured Query Language:结构化查询语言) 是用于管理关系数据库管理系统&#xff08;RDBMS&#xff09;。 SQL 的范围包括数据插入、查询、更新和删除&#xff0c;数据库模式创建和修改&#xff0c;以及数据访问控制。 SQL语法 数据库表 一个…

Autosar-从0到1构建Autosar最小系统(免费)-1

1建立Ecu最小系统 1.1Ecu最小系统组成 Ecu最小系统至少需要&#xff1a;SWC、Com、ComM、EcuM、BswM、Os、RTE等。 Autosar各模块的配置以arxml文件形式保存&#xff0c;因此生成以上模块需要以下各个arxml文件。 模块 所需的arxml文件 准备好以上arxml文件后&#xff0c;就可…

python核心篇之桌面程序

推荐使用: wxPython 一. 安装wxPython 二. 第一个wxPython程序 展示 代码 # coding: utf-8import wx# 创建应用程序对象 app wx.App() # 创建窗口对象 frame wx.Frame(None, title"Hello World", size(300, 200), pos(100, 100)) # 显示窗口 frame.Show() # 进入主…

【Spring实战项目】SpringBoot3整合WebSocket+拦截器实现登录验证!从原理到实战

&#x1f389;&#x1f389;欢迎光临&#xff0c;终于等到你啦&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;持续更新的专栏《Spring 狂野之旅&#xff1a;从入门到入魔》 &a…

CV论文--2024.4.3

1、Style Aligned Image Generation via Shared Attention 中文标题&#xff1a;共享注意力下的风格对齐图像生成 简介&#xff1a;大规模文本到图像&#xff08;T2I&#xff09;模型在创意领域迅速崭露头角&#xff0c;可以从文本提示中生成视觉上引人入胜的输出。然而&#…

【卫星家族】 | 高分六号卫星影像及获取

1. 卫星简介 高分六号卫星&#xff08;GF-6&#xff09;于2018年6月2日在酒泉卫星发射中心成功发射&#xff0c;是高分专项中的一颗低轨光学遥感卫星&#xff0c;也是我国首颗精准农业观测的高分卫星&#xff0c;具有高分辨率、宽覆盖、高质量成像、高效能成像、国产化率高等特…

C语言 | Leetcode C语言题解之第8题字符串转换整数atoi

题目&#xff1a; 题解&#xff1a; int myAtoi(char * s){int i0;int out0;int pol1;int lenstrlen(s);if(len0) return 0;while(s[i] ) i; //删除空格if(s[i]-){ //判断正负pol-1;i;}else if(s[i]){pol1;i;}else{pol1;}while(s[i]!\0){if(s[i]<0||s[i]>9){ /…

【Turtle】海龟先生

什么是编程 计算机只懂0和1这样的语言&#xff0c;可是我们不懂&#xff0c;当我们希望 计算要能帮我们做事情的时候&#xff0c;该怎么办呢&#xff1f; 我们需要一种更简便的方法告诉计算机要做什么&#xff0c;所以人类发明了编程语言 利用计算机编程语言&#xff0c;我们…

Transformer - 注意⼒机制

Transformer - 注意⼒机制 flyfish 计算过程 flyfish # -*- coding: utf-8 -*-import torch import torch.nn as nn import torch.nn.functional as F import os import mathdef attention(query, key, value, maskNone, dropoutNone):# query的最后⼀维的⼤⼩, ⼀般情况下就…

动态规划详解(Dynamic Programming)

目录 引入什么是动态规划&#xff1f;动态规划的特点解题办法解题套路框架举例说明斐波那契数列题目描述解题思路方式一&#xff1a;暴力求解思考 方式二&#xff1a;带备忘录的递归解法方式三&#xff1a;动态规划 推荐练手题目 引入 动态规划问题&#xff08;Dynamic Progra…

QT背景介绍

&#x1f40c;博主主页&#xff1a;&#x1f40c;​倔强的大蜗牛&#x1f40c;​ &#x1f4da;专栏分类&#xff1a;QT❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、QT背景 1.1什么是QT 1.2QT的发展历史 1.3什么是框架、库 1.4QT支持的平台 1.5QT的优点 1.6QT的…

分布式锁 — Redisson 全面解析!

前言 分布式锁主要是解决集群&#xff0c;分布式下数据一致性的问题。在单机的环境下&#xff0c;应用是在同一进程下的&#xff0c;只需要保证单进程多线程环境中的线程安全性&#xff0c;通过 JAVA 提供的 volatile、ReentrantLock、synchronized 以及 concurrent 并发包下一…

JVM_垃圾收集器

GC垃圾收集器 文章目录 GC垃圾收集器GC垃圾回收算法和垃圾收集器关系GC算法主要有以下几种四种主要的垃圾收集器SerialParallelCMSG1垃圾收集器总结查看默认垃圾收集器 默认垃圾收集器有哪些各垃圾收集器的使用范围部分参数说明 新生代下的垃圾收集器并行GC(ParNew)并行回收GC&…

[Python GUI PyQt] PyQt5快速入门

PyQt5快速入门 PyQt5的快速入门0. 写在前面1. 思维导图2. 第一个PyQt5的应用程序3. PyQt5的常用基本控件和布局3.1 PyQt5的常用基本控件3.1.1 按钮控件 QPushButton3.1.2 文本标签控件 QLabel3.1.3 单行输入框控件 QLineEdit3.1.4 A Quick Widgets Demo 3.2 PyQt5的常用基本控件…

morkdown语法转微信公众号排版(免费)

morkdown语法转微信公众号排版&#xff08;免费&#xff09; 源码来自githab&#xff0c;有些简单的问题我都修复了。大家可以直接去找原作者的源码&#xff0c;如果githab打不开就从我下载的网盘里下载吧。 效果

在制定OKR的过程中,应该怎么确定目标O的来源或方向?

在制定OKR&#xff08;Objectives and Key Results&#xff0c;目标与关键成果&#xff09;的过程中&#xff0c;确定目标O的来源或方向是至关重要的一步。一个明确、合理的目标能够为团队指明方向&#xff0c;激发团队成员的积极性和创造力&#xff0c;进而推动公司的整体发展…

【嵌入式智能产品开发实战】(十五)—— 政安晨:通过ARM-Linux掌握基本技能【GNU C标准与编译器】

目录 GNU C 什么是C语言标准 C语言标准的内容 C语言标准的发展过程 1.K&R C 2.ANSI C 3.C99标准 4.C11标准 编译器对C语言标准的支持 编译器对C语言标准的扩展 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: 嵌入式智能产品…

信息技术学院大数据技术专业开展专业实训周

四川城市职业学院讯&#xff08;信息技术学院 陈天伟&#xff09;日前&#xff0c;为提升学生的工匠精神和职业认知&#xff0c;信息技术学院邀请企业专家入驻眉山校区大数据实训基地&#xff0c;开展数据标识专业实训周。 数据标识是大数据专业的核心技术&#xff0c;数据标识…

在CentOS 7上安装Python 3.7.7

文章目录 一、实战步骤1. 安装编译工具2. 下载Python 3.7.7安装包3. 上传Python 3.7.7安装包4. 解压缩安装包5. 切换目录并编译安装6. 配置Python环境变量7. 使配置生效8. 验证安装是否成功 二、实战总结 一、实战步骤 1. 安装编译工具 在终端中执行以下命令 yum -y groupin…