C++11:声明 初始化

C++11:声明 & 初始化

    • 初始化
      • { }初始化
      • initializer_list
    • 声明
      • auto
      • decltype
      • nullptr


初始化

{ }初始化

在C++98中,允许使用花括号{ }对数组或者结构体元素进行统一的列表初始化。

{ }初始化数组:

int arr[] = { 1, 2, 3, 4, 5 };

{ }初始化结构体:

struct stu
{char name[20];int age;
};int main()
{stu s1 = { "Jones", 18 };return 0;
}

C++11扩大了{ }的适用范围,其可以用于所有的内置类型和自定义类型的初始化

C++11希望通过这个语法,使得所有变量可以以一种统一的方式进行初始化

比如对于内置类型:

int i = { 1 };
double d = { 3.14 };
char c = { 'x' };int* pf = { nullptr };
int& ri = { i };

我们可以通过大括号初始化整型,浮点型,指针,引用等等。在使用{ }进行初始化时,可以省略掉= ,所以以上代码也可以写为:

int i{ 1 };
double d{ 3.14 };
char c{ 'x' };int* pf{ nullptr };
int& ri{ i };

但是这个语法其实也比较鸡肋,因为这个写法完全没有直接int i = 1;这样来的直接。

接着就是自定义类型的初始化:

struct stu
{char name[20];int age;
};int main()
{stu s{ "jack", 18 };string str{ "hello world" };int arr[5]{ 1, 2, 3, 4, 5 };return 0;
}

以上代码中,通过直接在变量后面加一对大括号来实现初始化,数组和结构体初始化时的=也可以省略掉。

在此,我要额外辨析一下现有的类的初始化方式:

现在有如下日期类:

class Date
{
public:Date(int year, int month, int day): _year(year),_month(month),_day(day){}private:int _year;int _month;int _day;
};

其含义三个变量,表示年月日,一个三参数的构造函数,初始化这个Date

我们有如下方式对其初始化:

Date d1(2024, 4, 3);
Date d2 = { 2024, 4, 3 };
Date d3{ 2024, 4, 3 };

请问这三个方式,分别是如何初始化一个类的?

  • 对于Date d1(2024, 4, 3);,其就是最基础的构造函数调用语法,也就是直接构造
  • 对于Date d2 = { 2024, 4, 3 };,很多人看到这个写法,再想到我们刚才讲的{ }初始化,以为这个是C++11的新语法,其实并不是的。这个写法是多参数构造函数的类型转化,也就是说这个写法{ 2024, 4, 3 };就是把三个int类型转换为Date类型。如果你用explicit关键字修饰这个构造函数,那么类型转换功能就会被禁止,这个写法就会报错
  • 对于Date d3{ 2024, 4, 3 };,这个写法即使用了{ },而且还省略了=,这就是C++11提供的新语法了,当用explicit修饰这个构造函数,这个写法依然有效。因为这个写法也是直接调用构造函数,而不是进行类型转换

其实整体上来说,C++11提供{ }的意图在于提供统一的方式来初始化所有类型,但是奈何大部分程序员已经习惯了之前的写法,{ }既没有带来效率的提高,也没有更加人性化的语法设计(甚至我感觉int i = 1int i(1);更符合人类的习惯),因此这个语法并没有被广泛接受。


initializer_list

initializer_list是一个新的C++类型,我先为大家创建一个initializer_list类型:

auto li = { 1, 2, 3, 4 };

此时,li的类型就是initializer_list,这个时候有的人就疑惑了,{ 1, 2, 3, 4 }分明是一个整型数组,怎么改了个名字就变成新类型了?initializer_list翻译为中文就是初始化列表,也就是说,这是一个用于初始化的工具

假设现在你有以下数组:

int arr[5] = { 1, 2, 3, 4 };

你要如何用这个数组来初始化一个vector,初始化一个list,初始化一个set呢?

我们好像只能粗暴的遍历数组,然后一个一个插入数据:

vector<int> v;
for (int i = 0; i < 5; i++)
{v.push_back(arr[i]);
}

这着实有点麻烦了,但是C++11后,STL的所有容器都增加了新的构造函数,可以通过initializer_list来初始化容器:

vector<int> v({ 1, 2, 3, 4, 5 });

以上代码中,{ 1, 2, 3, 4, 5 }整体就是一个initializer_list,作为参数传给v,调用vector的构造函数。

当然,我们也可以这样写:

vector<int> v = { 1, 2, 3, 4, 5 };

这个写法,则是单参数的类型转化,因为{ 1, 2, 3, 4, 5 }整体就是一个initializer_list类型的参数。

相同的办法,我们还可以初始化map

map<string, string> m = { {"apple","苹果"}, {"strawberry","草莓"}, {"watermelon", "西瓜"} };

以上代码中,最外层的{ }括起来的就是一个initializer_list,内部的三个{ }则是三个不同的pair<const char*, const char*>,不过const char*可以转为string,因此最后pair<const char*, const char*>会变成pair<string, string>

最外层的initializer_list内部的三个pair,会依次插入进map中,也就是一次拿多个值初始化map的多个节点。

至此,你应该理解了,initializer_list就是在类构造时,如果我们想要一次性初始化多个节点,就把这些节点放进一个initializer_list内部,这样就能在构造函数中直接构造好。

initializer_list本质上也是一个容器,一个类模板:
在这里插入图片描述
因此{ 1, 2, 3, 4, 5 }的准确类型应该是:initializer_list<int>

initializer_list的底层也非常简单,我们看看其仅有的四个接口:

在这里插入图片描述

一个构造函数constructor,一个描述长度的接口size,以及迭代器beginend也就是说initializer_list本质上是一个通过迭代器访问数组的容器。当其它容器通过initializer_list构造自己,其实就是通过迭代器遍历那个存储了节点的数组,然后把数组元素一个一个插入。

也就是说,以下两种情况,本质是一样的:

initializer_list<int> lt = { 1, 2, 3, 4 };list<int> l1({ 1, 2, 3, 4 });
list<int> l2(lt.begin(), lt.end());

第一个list通过initializer_list初始化自己,第二个list则通过迭代器初始化自己。不过前者更加方便,是C++11提供的,而后者是C++98提供的。


声明

auto

在C++11中,新增了关键字auto,其可以自动推导类型:

auto i = 1;//整型
auto d = 3.14;//浮点型
auto p = &i;

此时i就会被自动识别为intd就自动识别为doublep自动识别为int*

auto的主要作用在于对于有一些类型,它的长度太长了,我们就可以用auto一笔带过。

比如完整地定义一个迭代器:

vector<int> v;
vector<int>::iterator it = v.begin();

但是我们可以用auto直接自动识别:

vector<int> v;
auto it = v.begin();

在定义迭代器的时候,auto的使用还是比较常见的。


decltype

在C++11以前,有一个关键字typeid,其可以识别一个类型,并且可以通过name成员函数来输出类型名。

比如这样:

int i = 0;
int* pi = &i;cout << typeid(i).name() << endl;
cout << typeid(pi).name() << endl;

输出结果为:

int
int * __ptr64

也就是说,我们可以通过typeid来检测甚至输出变量类型。

decltype也是用于识别类型的,但是decltypetypeid应用方向不同。

decltype可以检测一个变量的类型,并且拿这个类型去声明新的类型

比如这样:

int i = 0;
decltype(i) x = 5;

decltype(i)检测出i的类型为int,于是decltype(i)整体就变成int,从而定义出一个新的变量x


nullptr

在C++11后,推出了新的空指针nullptr,明明已经有NULL了,为啥还需要nullptr?

NULL在C语言中,表示的是((void*)0),也就是被强制转为void*类型的0。但是在C++中,NULL就是整数0

比如可以用刚才学的typeid验证一下:

cout << typeid(NULL).name() << endl;

输出结果为:int,这下就石锤了NULL在C++中就是int

这会导致不少问题,比如这样:

void func(int x)
{cout << "参数为整型" << endl;
}void func(void* x)
{cout << "参数为指针" << endl;
}int main()
{func(NULL);return 0;
}

以上代码中,func函数有两个重载,一个是参数为指针,一个是参数为整型。我现在就是想传一个空指针去调用指针版本的func。但是最后还是会调用int类型的。

nullptr不一样,nullptr不仅不是整型,而且其也不是void*。C++给了nullptr一个专属类型nullptr_t。这个类型有一个非常非常大的优势,该类型只能转化为其它指针类型,不能转化为指针以外的类型

比如以下代码:

int x1 = NULL;//正确
int x2 = nullptr;//错误

因为NULL本质是0,其可以转化为很多非指针类型,比如intdoublechar。但是nullptrnullptr_t,它只能转化为其他指针。上述代码中,我们把nullptr转化为一个int,此时编译器会直接报错,绝对禁止这个行为。

但是这样是可以的:

void* p1 = nullptr;
int* p2 = nullptr;
char* p3 = nullptr;
double* p4 = nullptr;

可以看到,nullptr保证了指针类型的稳定,空指针不会被传递到指针以外的类型。因此nullptr在各方面都有足够的优势,以更加安全的形式给用户提供空指针。


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

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

相关文章

详解2024年阿里云服务器租用价格表,最新报价

阿里云服务器租用价格表2024年最新&#xff0c;云服务器ECS经济型e实例2核2G、3M固定带宽99元一年&#xff0c;轻量应用服务器2核2G3M带宽轻量服务器一年61元&#xff0c;ECS u1服务器2核4G5M固定带宽199元一年&#xff0c;2核4G4M带宽轻量服务器一年165元12个月&#xff0c;2核…

C++中的List容器用法详解

文章目录 C中的List容器用法详解List 的特点List 的重要接口用法介绍1.创建和初始化Listlist 2.插入元素push_backpush_forntinsert 删除元素pop_backpop_fontclearerase 遍历List迭代器遍历范围for遍历 排序Listsort 反转Listreverse 转移Listsplice 去重unique 合并merge 总结…

网站可扩展架构设计——中台

从公众号转载&#xff0c;关注微信公众号掌握更多技术动态 --------------------------------------------------------------- 一、中台简介 1.传统项目架构的痛点 (1)重复造轮子 各项目相对独立&#xff0c;许多项目在重复造轮子&#xff0c;让项目本身越来越臃肿&#xf…

ts之基本类型,联合类型,函数,类的概念

1.ts中基础类型 数字 字符串 布尔 数组 元组 any never void null undefined 枚举 1.最基础的就是数字 字符串 布尔 number 和 大Number的区别 js特性 装箱的概念 xxx.xxx&#xff0c;string,boolean同理 let num1: number 1; let num2: Number 1; // 用来描述实例的 类也可…

精通并发【基础四】:创建线程的几种方式

在 Java 中&#xff0c;多线程编程是一种常见且强大的编程范式&#xff0c;它允许程序同时执行多个任务。创建线程是多线程编程的基础&#xff0c;Java 提供了几种不同的方法来创建和启动线程。本文将介绍三种常用的创建线程的方法&#xff1a;继承 Thread 类、实现 Runnable 接…

[Leetcode笔记] 动态规划相关

前言 写题目写到了一些和动态规划相关的内容&#xff0c;所以在这里记录一下 LCR 089 题解思路 总的来说&#xff0c;就是用一个数组去存储当前的最优解&#xff0c;然后从0开始一路向上顺推过去&#xff0c;求得最后一位的最优解。 class Solution { public:int rob(vect…

MT3016 竹鼠通讯

在真空中&#xff0c;一块无限平坦光滑绝缘不导热草地上有很多光滑且相同球形竹鼠&#xff0c;它们的坐标为&#xff08;xi​&#xff0c;yi​&#xff09;。竹鼠之间会通过脑电波联系彼此。现在请问相距最近两只竹鼠的直线距离分别是多少&#xff08;所有竹鼠都在草地的第一象…

检测头篇 | 利用RT-DETR模型的检测头去替换YOLOv8中的检测头

前言:Hello大家好,我是小哥谈。RT-DETR号称是打败YOLO的检测模型,其作为一种基于Transformer的检测方法,相较于传统的基于卷积的检测方法,提供了更为全面和深入的特征理解,将RT-DETR检测头融入YOLOv8,我们可以结合YOLO的实时检测能力和RT-DETR的深度特征理解能力,打造出…

VLAN基础讲解+不同VLAN间通信(实验)

第一章 VLAN基础 1.1 什么是VLAN 随着网络中计算机的数量越来越多&#xff0c;传统的以太网络开始面临广播泛滥以及安全性无法保证等各种问题。 VLAN即虚拟局域网&#xff0c;是将一个物理的局域网在逻辑上划分成多个广播域的技术。通过在交换机上配置VLAN&a…

基于深度学习的吸烟检测系统(网页版+YOLOv8/v7/v6/v5代码+训练数据集)

摘要&#xff1a;本文深入研究了基于YOLOv8/v7/v6/v5等深度学习模型的吸烟行为检测系统&#xff0c;核心采用YOLOv8并整合了YOLOv7、YOLOv6、YOLOv5算法&#xff0c;进行性能指标对比&#xff1b;详述了国内外研究现状、数据集处理、算法原理、模型构建与训练代码&#xff0c;及…

从0配置React

在本地安装和配置React项目&#xff0c;您可以使用create-react-app这个官方推荐的脚手架工具。以下是安装React的步骤&#xff0c;包括安装Node.js、使用create-react-app创建React应用&#xff0c;以及启动开发服务器。 下载安装node.js运行以下命令&#xff0c;验证Node.js…

【Qt 学习笔记】Qt 开发环境的搭建 | Qt 安装教程

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Qt 开发环境的搭建 | Qt 安装教程 文章编号&#xff1a;Qt 学习笔记 /…

外贸建站:WordPress搭建外贸独立站零基础自建站完整教程(2024)

对于做外贸来说&#xff0c;拥有自己的外贸独立网站真的非常重要。在外贸领域&#xff0c;如今各平台竞争激烈&#xff0c;规则多&#xff0c;成本高&#xff0c;价格战、政策变化快&#xff0c;还存在封店风险等等因素。在这种情况下&#xff0c;拥有外贸独立站就能很好规避上…

攻防世界[EASYHOOK]

阅读须知&#xff1a; 探索者安全团队技术文章仅供参考,未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作,由于传播、利用本公众号所提供的技术和信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者 本人负责&#xff0c;作者不为此承担任何责任,如…

STM32学习和实践笔记(4): 分析和理解GPIO_InitTypeDef GPIO_InitStructure (b)

继续上篇博文&#xff1a;STM32学习和实践笔记&#xff08;4&#xff09;: 分析和理解GPIO_InitTypeDef GPIO_InitStructure (a)-CSDN博客 往下写&#xff0c; 为什么&#xff1a;当GPIO_InitStructure.GPIO_PinGPIO_Pin_0 ; 时&#xff0c;其实就是将对应的该引脚的寄存器地…

关于一篇知乎答案的重现

〇、前言 早上在逛知乎的时候&#xff0c;瞥见了一篇答案&#xff1a;如何通俗解释Docker是什么&#xff1f;感觉很不错&#xff0c;然后就耐着性子看了下&#xff0c;并重现了作者的整个过程。但是并不顺利&#xff0c;记载一下这些坑。嫌麻烦的话可以直接clone 研究&#xf…

Xxxxxx

数据库 1&#xff0c;B树与B树区别 1&#xff0c;B树每个节点存ID与其他数据字段&#xff0c;B非叶子结点&#xff0c;只存ID&#xff0c;叶子结点存完整数据 好处&#xff1a;每个层级B树&#xff0c;可以存储更多的额数据&#xff0c;层级更少&#xff0c;更扁平&#xff…

Makefile:条件判断和循环的使用(十)

1、条件判断 ifeq&#xff1a;if equal的缩写&#xff0c;判断是否相等&#xff0c;相等返回true&#xff0c;不相等返回falseifneq&#xff1a;if not equal的缩写&#xff0c;判断是否不相等&#xff0c;不相等返回true&#xff0c;相等返回falseifdef&#xff1a;if defini…

【语义分割实战(1)】U-Net语义分割:训练自己的数据集

一、U-Net图像语义分割原理 UNet最早发表在2015的MICCAI会议上&#xff0c;4年多的时间&#xff0c;论文引用量已经达到了9700多次。 UNet成为了大多做医疗影像语义分割任务的baseline&#xff0c;同时也启发了大量研究者对于U型网络结构的研究&#xff0c;发表了一批基于UNet…

24. AUTOSAR MCAL分析(三)--Crypto Driver(1)

目录 1. Crypto Driver概述 2. 认识Crypto Driver Object 2.1 EB对CDO的实现 2.2 Vector对CDO的实现 2.3 小结 <