C++11【上】

news2024/12/24 2:12:37

在这里插入图片描述

欢迎来到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; // 输出 0
    std::cout << "Destination size: " << destination.size() << std::endl; // 输出 5

    return 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; // 输出 0
    std::cout << "Destination size: " << destination.size() << std::endl; // 输出 5

    return 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.coloradmin.cn/o/1252057.html

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

相关文章

用AI工具3分钟整理并制作出一本书的思维导图

本期教大家快速用AI工具制作出精美的思维导图。 1.用ChatGPT总结出书本内容 首先打开ChatGPT&#xff0c;在对话框中输入你想要它生成的内容&#xff0c;并且要求他以markdown代码的格式输出&#xff0c;只需要几十秒的时间。整本书的框架思维导图就生成了&#xff0c;你还可以…

大一统模型 Universal Instance Perception as Object Discovery and Retrieval 论文阅读笔记

Universal Instance Perception as Object Discovery and Retrieval 论文阅读笔记 一、Abstract二、引言三、相关工作实例感知通过类别名进行检索通过语言表达式的检索通过指代标注的检索 统一的视觉模型Unified Learning ParadigmsUnified Model Architectures 四、方法4.1 Pr…

【论文解读】Edit-DiffNeRF:使用2D-扩散模型编辑3D-NeRF

来源&#xff1a;投稿 作者&#xff1a;橡皮 编辑&#xff1a;学姐 论文链接&#xff1a;https://arxiv.org/abs/2306.09551 摘要 最近的研究表明&#xff0c;将预训练的扩散模型与神经辐射场&#xff08;NeRF&#xff09;相结合&#xff0c;是一种很有前途的文本到 3D 的生成…

“不得了·放飞杯” 2023年四川省健身健美锦标赛启动在成都隆重召开

“不得了放飞杯” 2023年四川省健身健美锦标赛启动在成都隆重召开 为了更好地推动四川省健身健美运动的普及和发展&#xff0c;结合《四川全民健身实施计划》的现状&#xff0c;适应新时代健身私教服务产业的发展需求&#xff0c;由中国健美协会指导&#xff0c;四川省健美健美…

使用SpringBoot集成FastDFS

使用SpringBoot集成FastDFS 这篇文章我们介绍如何使用 Spring Boot 将文件上传到分布式文件系统 FastDFS 中。 1、FastDFS FastDFS是一个开源的轻量级分布式文件系统&#xff0c;它对文件进行管理&#xff0c;功能包括&#xff1a;文件存储、文件同步、文件访问 &#xff0…

[修订版][工控]SIEMENS S7-200 控制交通红绿灯程序编写与分析

下载地址>https://github.com/MartinxMax/Siemens_S7-200_Traffic_Light 特别鸣谢接线过程实验目的题目要求I/O分配公式公式套用示例 程序分析分割块[不是必要的,自己分析用]左侧梯形图 [B1-B5]B1 [东西绿灯亮25s]B2 B3 B23 [东西绿灯闪烁3s]B4 [东西黄灯亮2s]B5 [东西红灯…

Labelme加载AI(Segment-Anything)模型进行图像标注

labelme是使用python写的基于QT的跨平台图像标注工具&#xff0c;可用来标注分类、检测、分割、关键点等常见的视觉任务&#xff0c;支持VOC格式和COCO等的导出&#xff0c;代码简单易读&#xff0c;是非常利用上手的良心工具。 第一步&#xff1a;   下载源码进行安装。 g…

float和double(浮点型数据)在内存中的储存方法

作者&#xff1a;元清加油 主页&#xff1a;主页 编译环境&#xff1a;visual studio 2022 (x86) 相信大家都知道数据在内存中是以二进制储存的 整数的储存方法是首位是符号位&#xff0c;后面便是数值位 那么浮点数在内存中是怎么储存的呢&#xff1f;我们先来看一个例子&am…

Python----函数的不定长参数--包裹位置参数*args、包裹关键字参数**kwargs

不定长参数 也叫 可变参数。用于不确定调用的时候会传递多少个参数(不传参也可以)的场景。此时&#xff0c;可用包裹(packing)位置参数&#xff0c;或者包裹关键字参数&#xff0c;来进行参数传递&#xff0c;会显得非常方便。 相关链接&#xff1a;Python---函数的参数类型--…

带你用uniapp从零开发一个仿小米商场_1.环境搭建

uniapp 介绍 uni-app 是一个使用 Vue.js 开发所有前端应用的框架&#xff0c;开发者编写一套代码&#xff0c;可发布到iOS、Android、Web&#xff08;响应式&#xff09;、以及各种小程序&#xff08;微信/支付宝/百度/头条/飞书/QQ/快手/钉钉/淘宝&#xff09;、快应用等多个…

【Python篇】详细讲解正则表达式

文章目录 &#x1f339;什么是正则表达式&#x1f354;语法字符类别重复次数组合模式 ✨例子 &#x1f339;什么是正则表达式 正则表达式&#xff08;Regular Expression&#xff09;&#xff0c;简称为正则或正则表达式&#xff0c;是一种用于匹配、查找和操作文本字符串的工…

还在犹豫Harmony要不要学?已有170万人参加官方培训

鸿蒙这么火&#xff0c;要不要学&#xff1f; 两百强的 App厂商&#xff0c;大部分接受了与鸿蒙的合作&#xff0c;硬件也有非常多与鸿蒙合作的厂商。鸿蒙的合作企业基本已经覆盖整个互联网客户的主流需求&#xff1b;所以鸿蒙的崛起不过是早晚的问题。随着鸿蒙4.0的升级&…

Linxu 进程替换

进程替换的背景&#xff1a; 进程的替换我们需要调用execl这个接口,exxecl在3号手册&#xff0c;属于系统接口。 调用系统命令 execl 为了方便理解execl的作用&#xff0c;我们写一个程序&#xff1a; 单进程替换 我们发现运行结果是通过c库里的exec接口把系统命令 "l…

3 Unsupervised learning recommenders reinforcement learning

文章目录 Week1Unsupervised LearningClusteringK-meansprincipleOptimization objectiveInitializing K-meanschose the number of clusters Anomaly DetectionFind unusual eventsAlgorithmchose epsilonAnomally Detection vs Supervised learningfeatures Week2Recommender…

LeetCode Hot100 114.二叉树展开为链表

题目&#xff1a; 给你二叉树的根结点 root &#xff0c;请你将它展开为一个单链表&#xff1a; 展开后的单链表应该同样使用 TreeNode &#xff0c;其中 right 子指针指向链表中下一个结点&#xff0c;而左子指针始终为 null 。展开后的单链表应该与二叉树 先序遍历 顺序相同…

力扣114. 二叉树展开为链表(java,用树模拟链表)

Problem: 114. 二叉树展开为链表 文章目录 题目描述思路解题方法复杂度Code 题目描述 给你二叉树的根结点 root &#xff0c;请你将它展开为一个单链表&#xff1a; 1.展开后的单链表应该同样使用 TreeNode &#xff0c;其中 right 子指针指向链表中下一个结点&#xff0c;而左…

C/C++ 通过SQLiteSDK增删改查

SQLite&#xff0c;作为一款嵌入式关系型数据库管理系统&#xff0c;一直以其轻量级、零配置以及跨平台等特性而备受青睐。不同于传统的数据库系统&#xff0c;SQLite是一个库&#xff0c;直接与应用程序一同编译和链接&#xff0c;无需单独的数据库服务器进程&#xff0c;实现…

FFmpeg命令分隔视频

有一个视频如a.mp4&#xff0c;此视频采用帧率为30生成&#xff0c;共有299帧&#xff0c;这里通过FFmpeg命令分隔成1秒一个个的小视频&#xff0c;即每个小视频帧数为30帧。 用到的FFmpeg参数如下所示&#xff1a; (1).-i:指定输入视频文件的名称&#xff1b; (2).-c:指…

BUUCTF [MRCTF2020]Ez_bypass 1

题目环境&#xff1a;F12查看源代码 I put something in F12 for you include flag.php; $flagMRCTF{xxxxxxxxxxxxxxxxxxxxxxxxx}; if(isset($_GET[gg])&&isset($_GET[id])) { $id$_GET[id]; $gg$_GET[gg]; if (md5($id) md5($gg) && $id ! $gg) { …

Node.js下载安装及配置镜像源

一、进入官网地址下载安装包 https://nodejs.org/dist 选择对应你系统的Node.js版本 这里我选择的是Windows系统、64位 二、安装程序 &#xff08;1&#xff09;下载完成后&#xff0c;双击安装包&#xff0c;开始安装Node.js (2)直接点【Next】按钮&#xff0c;此处可根据…