C++11【上】

在这里插入图片描述

欢迎来到Cefler的博客😁
🕌博客主页:那个传说中的man的主页
🏠个人专栏:题目解析
🌎推荐文章:题目大解析(3)

在这里插入图片描述


目录

  • 👉🏻 统一的列表初始化
  • 👉🏻 声明
    • auto
    • decltype
    • nullptr
  • 👉🏻范围for循环
  • 👉🏻智能指针
  • 👉🏻左右值引用和移动语义
    • 左值引用和右值引用认识
    • 左值引用与右值引用比较
      • move函数
    • 右值引用的移动语义
  • 👉🏻完美转发

👉🏻 统一的列表初始化

在C++98中,标准允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定。比如:

struct Point
{int _x;int _y;
};
int main()
{int array1[] = { 1, 2, 3, 4, 5 };int array2[5] = { 0 };Point p = { 1, 2 };return 0;
}

C++11扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自
定义的类型,使用初始化列表时,可添加等号(=),也可不添加

struct Point
{int _x;int _y;
};
int main()
{int x1 = 1;int x2{ 2 };int array1[]{ 1, 2, 3, 4, 5 };int array2[5]{ 0 };Point p{ 1, 2 };// C++11中列表初始化也可以适用于new表达式中int* pa = new int[4]{ 0 };return 0;
}

创建对象时也可以使用列表初始化方式调用构造函数初始化

class Date
{
public:Date(int year, int month, int day):_year(year),_month(month),_day(day){cout << "Date(int year, int month, int day)" << endl;}
private:int _year;int _month;int _day;};int main()
{Date d1(2022, 1, 1); // old style// C++11支持的列表初始化,这里会调用构造函数初始化Date d2{ 2022, 1, 2 };Date d3 = { 2022, 1, 3 };return 0;
}

👉🏻 声明

auto

auto 是 C++11 引入的关键字,用于进行自动类型推导。使用 auto 关键字可以让编译器自动推断变量的类型,而无需显式指定。这使得代码更简洁、可读性更强,并且有助于提高代码的灵活性。

使用 auto 的主要优势在于简化代码,特别是在涉及复杂的类型或模板时,可以避免手动书写冗长的类型声明。此外,auto 的使用还使得代码更容易维护,因为类型信息是根据初始化表达式自动生成的。

在使用 auto 时需要注意一些事项,例如:

  1. 自动类型推导: auto 关键字根据变量的初始化表达式推导其类型。因此,如果初始化表达式类型发生变化,auto 推导的类型也会相应变化。

  2. 类型推导规则: auto 会忽略顶层的 const 和引用,而保留底层 const。这意味着 auto 推导的类型可能不同于直接指定的类型。

  3. 避免过度使用: 尽管 auto 可以简化代码,但过度使用可能会降低代码的可读性。在一些情况下,显式指定类型可能更有利于代码的清晰性。

总体来说,auto 是 C++ 中的一项强大特性,能够简化代码并提高代码的灵活性。在使用时需要权衡简洁性和可读性,确保代码清晰易懂。

decltype

关键字decltype将变量的类型声明为表达式指定的类型。

// decltype的一些使用使用场景
template<class T1, class T2>
void F(T1 t1, T2 t2)
{
decltype(t1 * t2) ret;
cout << typeid(ret).name() << endl;
}
int main()
{
const int x = 1;
double y = 2.2;
decltype(x * y) ret; // ret的类型是double
decltype(&x) p;      // p的类型是int*
cout << typeid(ret).name() << endl;
cout << typeid(p).name() << endl;
F(1, 'a');
return 0;
}

nullptr

由于C++中NULL被定义成字面量0,这样就可能回带来一些问题,因为0既能指针常量,又能表示
整形常量。所以出于清晰和安全的角度考虑,C++11中新增了nullptr,用于表示空指针。

👉🏻范围for循环

范围for循环(Range-based for loop)是 C++11 引入的一种循环结构,它用于遍历容器(如数组、std::vectorstd::list 等)中的元素,提供了一种更为简洁和直观的方式。范围for循环的语法如下:

for (declaration : range) {// 循环体
}

其中,declaration 是一个变量声明,range 是一个可迭代的范围,例如容器。

举例来说,假设有一个整数数组,使用范围for循环遍历该数组:

#include <iostream>int main() {int numbers[] = {1, 2, 3, 4, 5};// 范围for循环遍历整数数组for (int num : numbers) {std::cout << num << " ";}return 0;
}

在这个例子中,int num 是在每次循环中声明的变量,而numbers 是要遍历的整数数组。

范围for循环的特点和优势包括:

  1. 简洁: 语法相对传统的for循环更为简洁,避免了索引的繁琐操作。

  2. 类型安全: 循环变量的类型由编译器自动推导,避免了类型错误。

  3. 容器遍历: 可以方便地遍历容器中的元素,包括数组、STL 容器等。

  4. 避免越界: 由于遍历的是容器的元素而不是索引,减少了越界的风险。

需要注意的是,范围for循环并不总是适用于所有情况,例如需要修改容器中的元素或需要访问元素的索引时,传统的for循环可能更为合适。

👉🏻智能指针

这个我们后续再讲。

👉🏻左右值引用和移动语义

左值引用和右值引用认识

左值(lvalue)和右值(rvalue)是表达式的两种基本分类,而左值引用和右值引用是与这两种表达式相关的引用类型。

  1. 左值(lvalue): 左值是一个具有标识符的表达式,可以取地址,即有确定的内存位置。通常来说,变量数组元素对象成员等都是左值。

对于左值,我们可以获取它的地址+可以对它赋值,左值可以出现赋值符号的左边,右值不能出现在赋值符号左边

int x = 42; // x 是左值
  1. 右值(rvalue): 右值是一个没有标识符、即将被销毁或没有确定内存位置的表达式。通常来说,字面常量表达式返回值函数返回值(这个不能是左值引用返回)等都是右值。

对于右值,右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,右值不能取地址

int result = 2 + 3; // 2 + 3 是右值

左值和右值的区分主要体现在它们的身份和生命周期上。

对应的,C++中引入了左值引用(lvalue reference)和右值引用(rvalue reference):

  1. 左值引用(lvalue reference): 左值引用绑定到左值,使用 & 表示。

    int x = 42;
    int &ref = x; // ref 是左值引用,绑定到左值 x
    

    左值引用主要用于传递可修改的左值参数或实现拷贝构造函数等场景。

  2. 右值引用(rvalue reference): 右值引用绑定到右值,使用 && 表示。

    int &&rref = 2 + 3; // rref 是右值引用,绑定到右值 2 + 3
    

    右值引用主要用于实现移动语义、完美转发等高效的资源管理。

在C++11之后,右值引用的引入对于实现移动语义和避免不必要的复制操作非常重要。移动语义通过将资源的所有权从一个对象转移到另一个对象,避免了深层次的复制。通过右值引用和移动语义,可以提高性能,尤其在处理大型数据结构时。

左值引用与右值引用比较

🥫 左值引用总结

  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;
}

move函数

std::move 是 C++ 标准库中的一个函数,用于将一个左值强制转换为右值引用。其声明位于头文件 <utility> 中。

template <typename T>
constexpr std::remove_reference_t<T>&& move(T&& t) noexcept;

std::move 接受一个模板参数 T,并返回 T&&,即右值引用类型。它的主要作用是告诉编译器将一个左值强制转换为右值引用,以便利用移动语义,避免不必要的拷贝操作。

移动语义是 C++11 引入的一个特性,通过将资源的所有权从一个对象转移到另一个对象,提高了对于大型数据结构的效率。

#include <utility>
#include <iostream>
#include <vector>int main() {std::vector<int> source = {1, 2, 3, 4, 5};// 使用 std::move 将左值 source 转换为右值引用std::vector<int> destination = std::move(source);// source 不再拥有数据,被移动到 destination 中std::cout << "Source size: " << source.size() << std::endl; // 输出 0std::cout << "Destination size: " << destination.size() << std::endl; // 输出 5return 0;
}

在这个例子中,std::move(source) 将左值 source 转换为右值引用,允许 destination 利用移动语义将 source 的内容移动到自己身上,而不进行深层次的拷贝

为什么可以将移动后的右值引用赋值给左值呢?

在 C++ 中,右值引用并不意味着它只能绑定到右值。右值引用是一种引用类型,可以绑定到右值,也可以绑定到左值(例如使用 std::move 转换后)。这种设计允许在需要移动语义的地方使用右值引用,同时保持对左值的兼容性。在移动后的对象上,其状态通常为“有效但未定义”的状态,所以为了防止在移动后继续使用被移动的对象,通常会将其置为一种已知的状态,如空或默认状态

右值引用的移动语义

右值引用的移动语义是 C++ 中引入的一种资源管理机制通过移动对象的底层资源而不是进行深层次的复制,以提高性能。这是通过使用右值引用和移动构造函数来实现的。

右值引用(Rvalue Reference):

在 C++11 中,引入了右值引用(Rvalue Reference),用 && 表示。与左值引用不同,右值引用主要用于绑定到临时对象、将要销毁的对象、字面常量等右值。

int&& x = 42;  // x 是右值引用,绑定到右值 42

移动构造函数

移动构造函数是一种特殊的构造函数,它允许从一个对象“窃取”底层资源而不是进行深层次的拷贝。移动构造函数通过右值引用参数接受被移动的对象,并将其底层资源从源对象“移动”到目标对象。通常,移动构造函数的实现会将源对象的指针置为空,以避免资源的重复释放

class MyString {
public:// 移动构造函数MyString(MyString&& other) noexcept: data(other.data), size(other.size) {other.data = nullptr; // 将源对象的指针置为空other.size = 0;}// 其他构造函数和成员函数
private:char* data;size_t size;
};

移动语义的应用

移动语义通常应用于需要大量内存操作的场景,例如处理大型容器、字符串等。通过移动而非拷贝,可以避免不必要的资源开销,提高程序的性能

#include <iostream>
#include <vector>int main() {std::vector<int> source = {1, 2, 3, 4, 5};// 移动构造函数被调用,避免了深层次的拷贝std::vector<int> destination = std::move(source);// source 不再拥有数据,被移动到 destination 中std::cout << "Source size: " << source.size() << std::endl; // 输出 0std::cout << "Destination size: " << destination.size() << std::endl; // 输出 5return 0;
}

在这个例子中,std::move(source) 使用了移动语义,将 source 的内容移动到 destination 中,避免了不必要的拷贝。这对于大型数据结构来说是非常高效的。

👉🏻完美转发

🌍模板中的&& 万能引用
模板中的&&不代表右值引用,而是万能引用其既能接收左值又能接收右值
模板的万能引用只是提供了能够接收同时接收左值引用和右值引用的能力,

但是引用类型的唯一作用就是限制了接收的类型后续使用中都退化成了左值
我们希望能够在传递过程中保持它的左值或者右值的属性, 就需要用我们下面学习的完美转发


完美转发是 C++ 中一个重要的概念,它允许保持参数的值类别(左值或右值)并正确地将其传递给其他函数。完美转发通常与引用折叠一同使用,它在 C++11 中引入了右值引用和模板的概念之后变得更为重要。

实现完美转发

实现完美转发通常涉及到引用折叠、std::forward 和模板类型推导。

  1. 引用折叠: 引用折叠是 C++ 中的一种规则,它决定了在某些情况下引用的最终类型。例如,T& && 折叠为 T&T&& && 折叠为 T&&

  2. std::forward: std::forward 是一个模板函数,用于在函数中保持参数的原始值类别。它通常与模板参数结合使用,确保参数以正确的方式传递。

🫧 例子

#include <utility>
#include <iostream>template <typename T>
void wrapper(T&& arg) {// 在这里使用 std::forward 保持参数的原始值类别process(std::forward<T>(arg));
}// 假设有一个处理函数 process
void process(int& i) {std::cout << "Lvalue reference: " << i << std::endl;
}void process(int&& i) {std::cout << "Rvalue reference: " << i << std::endl;
}int main() {int x = 42;// wrapper 函数通过 std::forward 完美转发参数到 process 函数wrapper(x);     // 传递左值,调用 process(int&)wrapper(42);    // 传递右值,调用 process(int&&)return 0;
}

在这个例子中,wrapper 函数通过 std::forward 完美转发参数到 process 函数。process 函数通过重载,可以正确地处理传递进来的左值和右值。

完美转发的能力是 C++ 中泛型编程的一个强大特性,它在很大程度上提高了代码的灵活性和可复用性。


如上便是本期的所有内容了,如果喜欢并觉得有帮助的话,希望可以博个点赞+收藏+关注🌹🌹🌹❤️ 🧡 💛,学海无涯苦作舟,愿与君一起共勉成长
在这里插入图片描述
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

unigui同页面内重定向跳转,企业微信内部应用开发获取用户code例子

procedure TMainForm.UniFormCreate(Sender: TObject); varurl: string;code: string; begin //如果没有code值&#xff0c;将进行重定向if UniApplication.Parameters.Values[code] thenbeginurl :https://open.weixin.qq.com/connect/oauth2/authorize?appid你们的企业ID&…

从0到0.01入门 Webpack| 004.精选 Webpack面试题

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

RuntimeError: CUDA error: device-side assert triggered

背景&#xff1a; 使用SAGEConv卷积层的图神经网络&#xff0c;网络架构如下 原因&#xff1a; 我在卷积层之前改变了特征矩阵的维度&#xff0c;原本为[172,1,32] 现在改为了 [172,2,32]。导致了特征矩阵x在进行 “x x.squeeze(1)” 操作时并没有将第二向量值去除&#xff08…

Proteus仿真--基于DS1302与1602LCD设计的可调式电子日历与时钟

本文介绍基于51单片机的DS1302与1602LCD可调式电子日历与时钟&#xff08;完整仿真源文件及代码见文末链接&#xff09; 仿真图如下 本设计中时间芯片选用DS1302芯片&#xff0c;液晶选用LCD1602模块&#xff0c;按键K1-K4&#xff0c;K1用于年月日时分选择&#xff0c;K2用于…

基于Scapy修改ClientHello的SNI(三)

需求:修改HTTPS的ClientHello中的SNI字段 目标:修改成功,wireshark显示正常 语言:Python 三方库:Scapy 下面是一个标准的ClientHello报文,是从一个完整的HTTPS流中保存出来的,原始报文中的SNI是www.baidu.com 在上一篇文章中 记录基于scapy构造ClientHello报文的尝试…

vue3+elementPlus之侧边菜单栏功能

选择默认的颜色&#xff0c;将代码拷贝至<el-aside>模块中 稍微把不需要的修改一下。 <template><div class"common-layout"><el-container><el-header class"homeHeader"><div class"headerTitle">Devops…

LiveGBS流媒体平台GB/T28181功能-查看国标设备会话列表直播会话、回放会话、下载会话、对讲会话

LiveGBS流媒体平台GB/T28181功能-查看国标设备会话列表直播会话、回放会话、下载会话、对讲会话 1、会话列表2、会话类型3、搭建GB28181视频直播平台 1、会话列表 LiveGBS-> 国标设备-》点击在线状态 点击会话列表 2、会话类型 下拉会话类型可以看到 直播会话、回放会话、…

Javaweb之Vue组件库Element的详细解析

4 Vue组件库Element 4.1 Element介绍 不知道同学们还否记得我们之前讲解的前端开发模式MVVM&#xff0c;我们之前学习的vue是侧重于VM开发的&#xff0c;主要用于数据绑定到视图的&#xff0c;那么接下来我们学习的ElementUI就是一款侧重于V开发的前端框架&#xff0c;主要用…

C语言—冒泡排序

方法一&#xff08;不使用函数解决&#xff09; #define _CRT_SECURE_NO_WARNINGS 1#include<stdio.h> int main() {int arr[]{15,52,23,0,5,6,45,8,9,10};int i0;int j0;for ( i 0; i < 9; i){int flag1; //flag判断数组元素是否有序&#xff0c;这里先假设…

【C++那些事儿】类与对象(3)

君兮_的个人主页 即使走的再远&#xff0c;也勿忘启程时的初心 C/C 游戏开发 Hello,米娜桑们&#xff0c;这里是君兮_&#xff0c;我之前看过一套书叫做《明朝那些事儿》&#xff0c;把本来枯燥的历史讲的生动有趣。而C作为一门接近底层的语言&#xff0c;无疑是抽象且难度颇…

微服务实战系列之Nginx(技巧篇)

前言 今天北京早晨竟然飘了一些“雪花”&#xff0c;定睛一看&#xff0c;似雪非雪&#xff0c;像泡沫球一样&#xff0c;原来那叫“霰”。 自然中&#xff0c;雨雪霜露雾&#xff0c;因为出场太频繁&#xff0c;认识门槛较低&#xff0c;自然不费吹灰之力&#xff0c;即可享受…

Debian 11.3 ARM64 安装中文语言包

文章目录 Debian 介绍1、执行命令2、语言选择3、修改设置 Debian 介绍 Debian是一种自由开源的操作系统&#xff0c;被广泛用于服务器、个人计算机和嵌入式设备。它是由全球志愿者组成的开发团队开发和维护的&#xff0c;以稳定性、安全性和自由性而闻名。 以下是一些关于Deb…