C++11常用知识分享(一)【列表初始化 || 简化声明 || 范围for || 左右值 || 可变参数模板】

目录

一. 列表初始化

1)用法

2) initializer_list

小节: 

二,简化声明

1) ,auto

2) ,decltype类

3),nullptr

三,范围for

四,C++11后,STL容器变化

五,左值与右值

1. 左值

2. 右值

3,左值与右值之间的比较

4. 右值引用使用场景

(1,将死右值的移动语义操作 

(2. 编译器传值返回优化

(3,将亡值做参数

(4. 完美转发

 5,默认成员函数

(1. 默认成员函数的强制生成

(2. 默认成员函数的禁止调用生成


嗨!收到一张超美的风景图,希望你每天都能顺心!  

一. 列表初始化

首先,我们需要区分的是,什么是初始化列表与列表初始化。 前者是在类对象创建时,对成员变量进行初始化。后者是C++11优化后添加的新功能。

C++中为了满足泛型编程,基础类型如int, double等内置类型都被重写成了类。这些类被称为包装类,它们提供了一些额外的功能,比如重载运算符、提供类型转换等,以便更好地支持泛型编程。

从下面的用法,我们其实可以窥见一二:

1)用法

其实在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 };
  // C++11中列表初始化也可以适用于new表达式中
  int* pa = new int[4]{ 0 };

  //自定义类型
   Point p{ 1, 2 };
   map<string, string> z1 = { {"li", "a"}, {"ze", "b"} };
   map<string, string> z2{ {"li", "a"}, {"ze", "b"} };
  return 0;
}
那这是如果实现的呢?

2) initializer_list

其实这个{ }本质上是一个特殊类,叫:std::initializer_list

介绍文档:initializer_list - C++ Reference (cplusplus.com)

那自定义类型,比如:vector如何利用initializer_list来实现列表初始化的呢?? 下面是vector构造函数,C++11的构造方法,在下面我们能看到我们要找的构造 

 

自己制作一个支持initializer_list的构造函数还是比较简单的:

但是我们也要注意:initializer_list中的变量是常量,在静态区,因此这些数据仅仅只能做数据值传递,无法修改。 

小节: 

C++11后,STL容器已经全部支持std::initializer_list列表初始化,作为构造函数的参数,使初始化容器对象就更方便了

二,简化声明

1) ,auto

对于我们来说auto已经是老朋友了,在C++11之前,变量的类型必定要显式地指定。然而,C++11引入了auto关键字,允许编译器根据变量的初始化表达式推断其类型。这意味着可以更加简洁地声明变量,减少了代码的冗余性和提高了代码的可读性。

例子 :

    map<string, string> z1 = { {"li", "a"}, {"ze", "b"} };
    
    // auto遍历
	for (auto& e : z1)  
	{
		cout << e.first << endl;
	}
    
    // 推导迭代器
	auto it = z1.begin();

2) ,decltype类

decltype的主要作用是推断表达式的类型,并且保留表达式的const和引用属性(auto 不能保留)。这使得我们可以使用decltype来声明变量、函数返回类型,以及在模板中推断函数返回类型等。

在泛型编程中,decltype可以帮助我们避免重复输入类型信息,提高代码的可读性和可维护性。它还可以用于推断lambda表达式的返回类型,以及在模板元编程中进行类型推断。

总之,decltype类的意义:提供了一种灵活的类型推断机制,使得C++编程更加方便和高效

3),nullptr

在C++11中添加了nullptr是为了解决空指针的歧义问题。在之前的C++版本中,使用NULL来表示空指针可能会引发一些问题,因为NULL可能被定义为0或者(void*)0,这样就会导致一些类型转换的问题

使用nullptr可以明确地表示空指针,避免了歧义,同时也可以提高代码的可读性和安全性。

三,范围for

范围for我们在遍历数据时,经常使用,其底层原理是迭代器。有了范围for后,使用范围for循环可以更加直观简洁地遍历容器或数组中的元素,而不需要手动管理迭代器或索引变量。这使得代码更易读、易维护,并且减少了出错的可能性。范围for循环也可以与自定义类型一起使用,只要该类型支持迭代操作。因此,范围for功能的添加使得C++代码更加现代化和易用。

四,C++11后,STL容器变化

 前面所学的STL中,现在回望这些C++11才出现的容器。

还有一些比如:右值引用等等,这个我们后面细讲。

五,左值与右值

1. 左值

左值是一个表示数据的表达式(如变量名或解引用的指针), 我们 可以获取它的地址 +可以对它赋值,左值可以出现赋值符号的左边,右值不能出现在赋值符号左边。定义时const修饰符后的左值,不能给他赋值,但是可以取它的地址。左值引用就是给左值的引用,给左值取别名。
int main()
{
  // 以下的p、b、c、*p都是左值
  int* p = new int(0);
  int b = 1;
  const int c = 2;

  // 以下几个是对上面左值的左值引用
  int*& rp = p;
  int& rb = b;
  const int& rc = c;
  int& pvalue = *p;
  return 0;
}

传统的C++语法中就有引用的语法,而C++11中新增了的右值引用语法特性,所以从现在开始我们之前学习的引用就叫做左值引用无论左值引用还是右值引用,都是给对象取别名

2. 右值

右值也是一个表示数据的表达式,如: 字面常量、表达式返回值,函数返回值(这个不能是左值引用返回)等等, 右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,右值不能取地址。右值引用就是对右值的引用,给右值取别名。
int main()
{
   double x = 1.1, y = 2.2;
 // 以下几个都是常见的右值
   10;
   x + y;
   fmin(x, y);

 // 以下几个都是对右值的右值引用
   int&& rr1 = 10;
   double&& rr2 = x + y;
   double&& rr3 = fmin(x, y);

 // 这里编译会报错:error C2106: “=”: 左操作数必须为左值
   10 = 1;
   x + y = 1;
   fmin(x, y) = 1;
 return 0;
}

3,左值与右值之间的比较

左值 引用总结:
  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;
}

4. 右值引用使用场景

聊使用场景前我们需要先更深入的了解右值引用: 右值是不可获取地址的,同时我们在用左值引用时,因为其不可修改性,还必须添加 const 才能接收。那我们思考下面代码

补充: 

因此,我们可以这么理解右值引用是用来修改右值的工具,那什么场景下使用该工具?  请观察下面是简易实现的string类代码:

(1,将死右值的移动语义操作 

namespace bit
{
  class string
  {
   public:
   typedef char* iterator;
 iterator begin() {return _str;}
 iterator end(){return _str + _size;}

 string(const char* str = "")
 :_size(strlen(str))
 , _capacity(_size)
 {
 //cout << "string(char* str)" << endl;
 _str = new char[_capacity + 1];
 strcpy(_str, str);
 }

 // s1.swap(s2)
 void swap(string& s){::swap(_str, s._str);
 ::swap(_size, s._size);
 ::swap(_capacity, s._capacity);}

 // 拷贝构造
 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;}

 ~string(){
  delete[] _str;
  _str = nullptr;}

 char& operator[](size_t pos)
 {assert(pos < _size);
   return _str[pos];}

 void reserve(size_t n){if (n > _capacity)
 {char* tmp = new char[n + 1];
  strcpy(tmp, _str);
  delete[] _str;
  _str = tmp;
  _capacity = n;}}

 void push_back(char ch){if (_size >= _capacity)
 {size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
 reserve(newcapacity);}
  _str[_size] = ch;
  ++_size;
  _str[_size] = '\0';}

 //string operator+=(char ch)
 string& operator+=(char ch){push_back(ch);
 return *this;}

 const char* c_str() const{return _str;}

 private:
 char* _str;
 size_t _size;
 size_t _capacity; // 不包含最后做标识的\0
 };
}

我们用右值引用,从引用中区分出将死右值引用,同时重载赋值函数移动语义,间接达到了减少一次拷贝的功能,解析如下: 

因此,自定义类型的左值经过move库函数操作后, 被转化为了将亡右值后,未来可能出现将亡右值数据被替换,因此move慎用!! 

(2. 编译器传值返回优化

面对局部对象的传值返回的性能消耗,编译器也做了优化,如下:

 当然这种优化跟编译器的版本,新旧有关,我们了解即可。

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

(3,将亡值做参数

对于参数是 将亡右值的插入接口,C++11就可以用 移动拷贝提高效率。

对于我们使用接口来说,STL 容器插入接口函数也增加了右值引用版本:
http://www.cplusplus.com/reference/list/list/push_back/
http://www.cplusplus.com/reference/vector/vector/push_back/

(4. 完美转发

模板中的&& 万能引用  

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

但万能引用,却不一定“万能”,模板的万能引用只是提供了能够接收同时接收左值引用和右值引用的能力,不代表就能表示左右值引用类型,引用类型的唯一作用就是限制了接收的类型,后续使用中都退化成了左值,我们希望能够在传递过程中保持它的左值或者右值的属性, 就需要用我们下面学习的完美转发。

头文件:utility

用万能引用接受过数据,尽量每次用完美转发的形式重新获取准确数据类型再作为参数。

 5,默认成员函数

原来 C++ 类中,有 6 个默认成员函数:
1. 构造函数
2. 析构函数
3. 拷贝构造函数
4. 拷贝赋值重载
5. 取地址重载
6. const 取地址重载
最后重要的是前 4 个,后两个用处不大。默认成员函数就是我们不写编译器会生成一个默认的。
C++11 新增了两个:移动构造函数和移动赋值运算符重载。
针对移动构造函数和移动赋值运算符重载有一些需要注意的点如下:
1. 没有自己实现 移动构造 函数,且析构函数 、拷贝构造、拷贝赋值重载 都未实现 。那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝;自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造。
2. 没有自己实现 移动赋值重载 函数,且析构函数 、拷贝构造、拷贝赋值重载 都未实现 ,那么编译器会自动生成一个默认移动赋值默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝;自定义类型成员,则需要看这个成员是否实现移动赋值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。( 默认移动赋值跟上面移动构造完全类似)
如果你提供了移动构造 者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值。

(1. 默认成员函数的强制生成

关键字:  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)
	 {
		 cout << "拷贝构造" << endl;
	 }

	Person(Person&& p) = default; // 强制生成移动构造
	Person& operator=(Person&& p) = default; // 移动赋值基于移动构造
 private:
	 string _name;
	 int _age;
 };
 int main()
 {
	 Person s1("1111", 2);
	 Person s2 = s1;
	 Person s3 = std::move(s1);
	 return 0;
 }

(2. 默认成员函数的禁止调用生成

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

结语

   本小节就到这里了,感谢小伙伴的浏览,如果有什么建议,欢迎在评论区评论,如果给小伙伴带来一些收获请留下你的小赞,你的点赞和关注将会成为博主创作的动力

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【数据结构】实现堆

大家好&#xff0c;我是苏貝&#xff0c;本篇博客带大家了解堆&#xff0c;如果你觉得我写的还不错的话&#xff0c;可以给我一个赞&#x1f44d;吗&#xff0c;感谢❤️ 目录 一. 堆的概念及结构二. 堆的实现堆的结构体初始化销毁插入数据删除数据&#xff08;默认删除堆顶即…

【JS】WebSocket实现简易聊天室

【JS】WebSocket实现简易聊天室 聊天室思路示例 聊天室思路 聊天室思路 1、连接服务器先建立连接&#xff0c;默认生成匿名用户(admin01) 2、客户端发送消息&#xff0c;其它客户端用户都会同步接收消息(服务端接受消息广播所有连接用户) 3、客户端修改昵称&#xff0c;其它客…

鸿蒙应用组件

基础组件 索引组件—AlphabetIndexer&#xff08;相当于安卓的seedbar&#xff09; 使用&#xff1a;AlphabetIndexer(value: {arrayValue: Array<string>, selected: number})空白填充组件—Blank&#xff08;占位使用&#xff0c;当父组件为Row/Column/Flex时生效&am…

[SpringCloud] OpenFeign核心架构原理 (一)

Feign的本质: 动态代理 七大核心组件 Feign底层是基于JDK动态代理来的, Feign.builder()最终构造的是一个代理对象, Feign在构建对象的时候会解析方法上的注解和参数, 获取Http请求需要用到基本参数以及和这些参数和方法参数的对应关系。然后发送Http请求, 获取响应, 再根据响…

2024-3-4 市场分歧视角

今天市场有一个单独分歧视角可以观察思考&#xff0c;竞价氢能这边严重不符合预期&#xff0c;隔夜单 四川金顶 和 东方精工 大幅减少&#xff0c;预期就是这两个货会高位分歧&#xff0c;最高板 东方精工 开盘就是瀑布杀&#xff0c;四川金顶先杀到1个多点&#xff0c;9&#…

分库分表如何管理不同实例中几万张分片表?

在进行分库分表设计时&#xff0c;确认好了数据节点数量和分片策略以后&#xff0c;接下来要做的就是管理大量的分片表。实际实施过程中可能存在上百个分片数据库实例&#xff0c;每个实例中都可能有成千上万个分片表&#xff0c;如果仅依靠人力来完成这些任务显然是不现实的。…

【AI视野·今日Robot 机器人论文速览 第八十期】Fri, 1 Mar 2024

AI视野今日CS.Robotics 机器人学论文速览 Fri, 1 Mar 2024 Totally 32 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Robotics Papers Humanoid Locomotion as Next Token Prediction Authors Ilija Radosavovic, Bike Zhang, Baifeng Shi, Jathushan Rajasegaran…

Linux--文件(2)-重定向和文件缓冲

命令行中的重定向符号 介绍和使用 在Linux的命令行中&#xff0c;重定向符号用于将命令的输入或输出重定向到文件或设备。 常见的重定向符号&#xff1a; 1.“>“符号&#xff1a;将命令的标准输出重定向到指定文件中&#xff0c;并覆盖原有的内容。 2.”>>“符号&a…

《高效使用Redis》- 由面试题“Redis是否为单线程”引发的思考

由面试题“Redis是否为单线程”引发的思考 很多人都遇到过这么一道面试题&#xff1a;Redis是单线程还是多线程&#xff1f;这个问题既简单又复杂。说他简单是因为大多数人都知道Redis是单线程&#xff0c;说复杂是因为这个答案其实并不准确。 难道Redis不是单线程&#xff1f…

springboot项目单纯使用nacos注册中心功能

Spring Boot 项目完全可以单独使用 Nacos 作为注册中心。Nacos 是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。它支持服务的注册与发现&#xff0c;能够与 Spring Boot 应用无缝集成&#xff0c;为微服务架构提供了强大的支持。 在使用 Nacos 作为注册中…

#QT(串口助手-界面)

1.IDE&#xff1a;QTCreator 2.实验&#xff1a;编写串口助手 3.记录 接收框:Plain Text Edit 属性选择&#xff1a;Combo Box 发送框:Line Edit 广告&#xff1a;Group Box &#xff08;1&#xff09;仿照现有串口助手设计UI界面 &#xff08;2&#xff09;此时串口助手大…

从0搭建Azure DevOps Server

Windows虚拟机搭建DevOps 服务器 背景资源准备安装软件需求流程版本兼容性安装SQL ServerSSMS安装visual StudioAzure DevOps Server测试本地访问端口更改及外界访问 背景 搭建一台Azure DevOps Server 供我们运维项目开发&#xff0c;现在DevOps运维已成为一个主流&#xff0…

【金三银四】每日一点面试题(Java--JVM篇)

1、说一下 JVM 的主要组成部分及其作用&#xff1f; JVM&#xff08;Java虚拟机&#xff09;是Java程序运行的核心组件&#xff0c;它负责将Java字节码翻译成底层操作系统能够执行的指令。JVM由以下几个主要组成部分构成&#xff1a; 类加载器&#xff08;Class Loader&#…

117.移除链表元素(力扣)

题目描述 代码解决 class Solution { public:ListNode* removeElements(ListNode* head, int val) {//删除头节点while(head!NULL&&head->valval){ListNode*tmphead;headhead->next;delete tmp;}//删除非头节点ListNode*curhead;while(cur!NULL&&cur-&g…

【python】python用户管理系统[简易版](源码+报告)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

End-to-End Weakly-Supervised SemanticSegmentation with Transformers

摘要 弱监督语义分割&#xff08;WSSS&#xff09;使用图像级标签是一项重要且具有挑战性的任务。由于高训练效率&#xff0c;端到端的WSSS解决方案受到社区越来越多的关注。然而&#xff0c;当前的方法主要基于卷积神经网络&#xff0c;并未正确地探索全局信息&#xff0c;因…

SwiftUI 在 App 中弹出全局消息横幅(下)

功能需求 在 SwiftUI 开发的 App 界面中,有时我们需要在全局层面向用户展示一些消息: 如上图所示:我们弹出的全局消息横幅位于所有视图之上,这意味这它不会被任何东西所遮挡;而且用户可以点击该横幅关闭它。这是怎么做到的呢? 在本篇博文中,您将学到以下内容 功能需求…

靶机渗透之Misdirection

Name: Misdirection: 1Date release: 24 Sep 2019Author: FalconSpySeries: MisdirectionDownload (Mirror): https://download.vulnhub.com/misdirection/Misdirection.zip 对于vulnhub中的靶机&#xff0c;我们都需先下载镜像&#xff0c;然后导入VM&#xff0c;并将网络连接…

简要讲解OV7725摄像头

本文主要包含以下几部分内容&#xff1a; 1. 通过OV7725分析模块原理图。 2. 讲解部分寄存器的含义、RGB565格式图像输出时序、帧率计算。 3. 讲解SCCB协议与I2C协议的区别。 1、OV7725功能 OV7725是一款1/4英寸单芯片图像传感器&#xff0c;其感光阵列达到640*480&#xff0c…

【Python】Python教师/学生信息管理系统 [简易版] (源码)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…
最新文章