【C++ 学习 ㉘】- 详解 C++11 的列表初始化

目录

一、C++11 简介

二、列表初始化

2.1 - 统一初始化

2.2 - 列表初始化的使用细节

2.2.1 - 聚合类型的定义

2.2.2 - 注意事项

2.3 - initializer_list

2.3.1 - 基本使用

2.3.2 - 源码剖析


 


一、C++11 简介

  1. 1998 年,C++ 标准委员会发布了第一版 C++ 标准,即 C++98 标准,并计划以后每 5 年视实际需要更新一次标准。

    所谓标准,即明确 C++ 代码的编写规范,所有的 C++ 程序员都应遵守此标准

  2. 2003 年,C++ 标准委员会发布了第二版 C++ 标准,即 C++03 标准,但由于 C++03 仅修复了一些 C++98 中存在的漏洞,并未修改核心语法,因此人们习惯将这两个标准合称为 C++98/03 标准。

  3. 2011 年,C++ 标准委员会发布了第三版 C++ 标准,即 C++11 标准,相比 C++03,C++11 带来了数量可观的变化,其中包含了约 140 个新特性,以及对 C++03 中约 600 个缺陷的修正,这使得 C++11 更像从 C++98/03 中孕育出来的一种新语言。。

    C++ 标准委员会一开始是计划在 2007 年发布第三版 C++ 标准,即 C++07 标准,但在 2006 年时,标准委员会认为到 2007 年,甚至到 2008 年,都可能无法发布第三版 C++ 标准,所以干脆将第三版 C++ 标准命名为 C++0x,即计划在二十一世纪的第一个 10 年的某个时间发布,但最终直到 2011 年才发布第三版 C++ 标准


二、列表初始化

在 C++98/03 中,对象的初始化方式有很多种,这些不同的初始化方式都有各自的适用范围和作用,没有一种方式可以通用于所有情况。为了统一初始化方式,并且让初始化行为具有确定的效果,C++11 提出了列表初始化的概念

2.1 - 统一初始化

在 C++98/03 中,对于普通数组和可以直接进行内存拷贝(memcpy)的对象,可以使用列表初始化来初始化数据。

int arr[5] = { 0, 1, 2, 3, 4 };
​
struct Point
{int _x;int _y;
} p = { 0, 0 };

在 C++11 中,初始化列表的适用性被大大地增加了,它现在可以适用于任何类型对象的初始化

注意:在 C++11 中,使用列表初始化时,可以添加等号(=),也可以不添加等号

class A
{
public:A(int i = 0) : _i(i) { }
private:A(const A& a) : _i(a._i) { }
private:int _i;
};
​
int main()
{A a1(10);A a2 = 10;A a3 = { 10 };A a4{ 10 };return 0;
}
  1. a3、a4 使用了 C++11 的列表初始化来初始化对象,效果如同 a1 的直接初始化

  2. 至于 a2,10 会通过隐式类型转换调用构造函数 A(int i = 0) 构造出一个匿名对象,然后通过这个匿名对象调用拷贝构造函数 A(const A& a) 构造出 a2,但由于拷贝构造函数是私有的(private),所以编译器会报错

    注意:Linux 中的 g++ 编译器会报错,VS 中的编译器则不会报错

使用 new 操作符创建新对象时也可以使用列表初始化来初始化对象

int* p = new int{ 0 };
int* arr = new int[5]{ 0, 1, 2, 3, 4 };

除了上面所述的内容,列表初始化还可以直接用在函数传参和返回值上

#include <iostream>
#include <string>
using namespace std;
​
class Person
{
public:Person(int id, string name) : _id(id), _name(name){cout << _id << ":" << _name << endl;}
private:int _id;string _name;
};
​
void func1(Person p) { }
​
Person func2() { return { 2, "李四" }; }
​
int main()
{func1({ 1, "张三" });  // 1:张三Person p = func2();  // 2:李四return 0;
}

 

2.2 - 列表初始化的使用细节

在 C++11 中,列表初始化的使用范围被大大地增加了,但一些模糊的概念也随之而来。

#include <iostream>
using namespace std;
​
struct T1
{int _x;int _y;
} t1{ 520, 520 };
​
struct T2
{int _x;int _y;
​T2(int, int) : _x(1314), _y(1314) { }
} t2{ 520, 520 };
​
int main()
{cout << t1._x << ", " << t1._y << endl;  // 520, 520cout << t2._x << ", " << t2._y << endl;  // 1314, 1314return 0;
}

在上面的程序中,t1 和 t2 都使用相同的列表初始化来初始化对象,但输出的结果却不同。因为对于聚合类型的对象 t1,它可以直接使用列表初始化来初始化对象;对于非聚合类型的对象 t2,它是基于构造函数使用列表初始化来初始化对象

2.2.1 - 聚合类型的定义

  1. 普通数组可以看作是一个聚合类型。

  2. 满足以下条件的类(class、struct、union)可以看作是一个聚合类型:

    • 无基类、无虚函数以及无用户自定义的构造函数

    • 无 private 或 protected 的普通数据成员(即非静态数据成员)

      struct T1
      {int _x;int _y;
      private:  // 或者 protectedint _z;
      } t1{ 1, 2, 3 };  // error(类中有私有成员,无法使用列表初始化进行初始化)
      ​
      struct T2
      {int _x;int _y;
      protected:  // 或者 protectedstatic int _z; 
      } t2{ 1, 2 };  // ok
      ​
      int T2::_z = 3;  // 注意:静态数据成员 _z 不能使用列表初始化进行初始化
    • 类中不能有 {} 和 = 直接初始化的非静态数据成员(即就地初始化)

      struct T3
      {int _x = 1;int _y{ 2 };
      } t3{ 0, 0 };  // error(C++11)

      注意:从 C++14 开始,也可以使用列表初始化来初始化类中使用 {} 和 = 初始化过的非静态数据成员

2.2.2 - 注意事项

聚合类型的定义并非递归的,即当一个类的非静态数据成员是非聚合类型时,这个类也可能是聚合类型

struct T1
{int _x;int _y;
private:int _z;
public:T1() : _x(1), _y(2), _z(3) { }
};
​
struct T2
{T1 _t1;double _d;
};
​
int main()
{T2 t2{ {}, 3.14 };return 0;
}

可以看到,T1 是非聚合类型,因为它有一个 private 的非静态数据成员,但 T2 依然是一个聚合类型,可以直接使用列表初始化来初始化对象 t2。

注意:使用列表初始化来初始化 t2 的非聚合类型成员 _t1 时,可以直接写一对空的大括号 {},这相当于调用 _t1 的默认构造函数

2.3 - initializer_list

2.3.1 - 基本使用

当编译器看到 { t1, t2, ..., tn } 时,便会生成一个 initializer_list<T> 类型的对象(其中 T 为元素的类型),它关联到一个 array<T, n>

#include <iostream>
using namespace std;
​
int main()
{auto il = { 10, 20, 30 };cout << typeid(il).name() << endl;  // class std::initializer_list<int>return 0;
}

对于聚合类型,编译器会将 array<T, n> 内的元素逐一分解并赋值给被初始化的对象,这相当于为该对象每个字段分别赋值

对于非聚合类型,如果该类存在一个接收 initializer_list<T> 类型的构造函数,则初始化时会将 initializer_list<T> 对象作为一个整体传给构造函数;如果该类不存在这样的构造函数,则 array<T, n> 内的元素会被编译器分解并传给相应的能接收这些参数的构造函数(比如列表中有 2 个元素,就传给带 2 个参数的构造函数,有 3 个元素,就传给带 3 个参数的构造函数,依次类推)

#include <iostream>
#include <vector>
using namespace std;
​
class Test
{
public:Test(int) { cout << "Test(int)" << endl; }
​Test(int, int) { cout << "Test(int, int)" << endl; }
};
​
int main()
{// vector (initializer_list<value_type> il, //      const allocator_type& alloc = allocator_type());vector<int> v{ 0, 1, 2, 3, 4 };for (const auto& e : v){cout << e << " ";}// 0 1 2 3 4cout << endl;Test t1{ 1 };  // Test(int)Test t2{ 1, 2 };  // Test(int, int)return 0;
}

2.3.2 - 源码剖析

#include <iostream>
​
template <class T>
class initializer_list
{
public:typedef T        value_type;typedef const T& reference;  // 说明对象永远为 const,不能被外部修改!typedef const T& const_reference;typedef size_t   size_type;typedef const T* iterator;  // 永远为 const 类型typedef const T* const_iterator;private:iterator  _M_array; // 用于存放用列表初始化中的元素size_type _M_len;   // 元素的个数
​// 注意:编译器可以调用 private 的构造函数!!!// 构造函数在调用之前,编译会先在外部准备好一个 array,// 同时把 array 的地址传入模板,并保存在 _M_array 中constexpr initializer_list(const_iterator __a, size_type __l):_M_array(__a), _M_len(__l) {};  // 注意该构造函数被放到 private 中!public:// 无参的构造函数constexpr initializer_list() : _M_array(0), _M_len(0) {} 
​// 用于获取元素的个数constexpr size_type size() const noexcept { return _M_len; }
​// 获取第一个元素的位置constexpr const_iterator begin() const noexcept { return _M_array; }
​// 获取最后一个元素的下一个位置constexpr const_iterator end() const noexcept{return begin() + _M_len;}
};

让模拟实现的 vector 也支持列表初始化

namespace yzz
{template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;
​vector(std::initializer_list<T> il) :_start(new T[il.size()]),_finish(_start + il.size()),_end_of_storage(_finish){iterator v_it = _start;typename std::initializer_list<T>::iterator il_it = il.begin();while (il_it != il.end()){*v_it++ = *il_it++;}}// ... ...private:iterator _start;iterator _finish;iterator _end_of_storage;};
}

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

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

相关文章

大数据Hadoop之——部署hadoop+hive+Mysql环境(window11)

一、安装JDK8 【温馨提示】对应后面安装的hadoop和hive版本&#xff0c;这里使用jdk8&#xff0c;这里不要用其他jdk了&#xff0c;可能会出现一些其他问题。 1&#xff09;JDK下载地址 Java Downloads | Oracle 按正常下载是需要先登录的&#xff0c;这里提供一个不用登录下载…

VMware虚拟机安装Linux系统的介绍

许多新手连 Windows 的安装都不太熟悉&#xff0c;更别提 Linux 的安装了&#xff1b;即使安装成功了&#xff0c;也有可能破坏现有的 Windows 系统&#xff0c;比如导致硬盘数据丢失、Windows 无法开机等。所以一直以来&#xff0c;安装 Linux 系统都是初学者的噩梦。 然而&a…

Zookeeper【Curator客户端Java版】从0到1——万字学习笔记

目录 初识Zookeeper Zookeeper作用 维护配置信息 分布式锁服务 集群管理 生产分布式唯一ID Zookeeper的设计目标 Zookeeper 工作机制 数据模型 ZooKeeper 命令操作 服务端常用命令 客户端常用命令 ZooKeeper JavaAPI操作 Curator 介绍 Curator API 常用操作 导入依赖 建立连接 …

PTE-精听学习(一)

目录 SST SST每一题都是单独计时 MMA 切换题目的时候&#xff0c;总是会迷茫 deduct 出现关键词之后&#xff0c;才开始精听 没有人管你 &#xff0c;绝对是要为后方留出更多的时间 &#xff0c;选多一个错的&#xff0c;要倒扣分 特征 1.paraphrase 2.循序出现 …

JDK 21的新特性总结和分析

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

Windows服务器安装php+mysql环境的经验分享

php mysql环境 下载IIS Php Mysql环境集成包,集成包下载地址: 1、Windows Server 2008 一键安装Web环境包 x64 适用64位操作系统服务器:下载地址:链接: https://pan.baidu.com/s/1MMOOLGll4D7Eb5tBrdTQZw 提取码: btnx 2、Windows Server 2008 一键安装Web环境包 32 适…

视频推拉流/直播点播平台EasyDSS分享的链接提示“无信号”,该如何解决?

视频直播点播平台EasyDSS可支持用户自行上传视频文件&#xff0c;也可将上传的点播文件作为虚拟直播进行播放。平台能支持多屏播放&#xff0c;可兼容Windows、Android、iOS、Mac等操作系统&#xff0c;还能支持CDN转推&#xff0c;具备较强的可拓展性与灵活性。 为给用户提供更…

AcWing算法提高课-5.6.2青蛙的约会

宣传一下 算法提高课整理 CSDN个人主页&#xff1a;更好的阅读体验 原题链接 题目描述 两只青蛙在网上相识了&#xff0c;它们聊得很开心&#xff0c;于是觉得很有必要见一面。 它们很高兴地发现它们住在同一条纬度线上&#xff0c;于是它们约定各自朝西跳&#xff0c;直到…

数据库设计与前端框架

数据库设计与前端框架 学习目标&#xff1a; 理解多租户的数据库设计方案 熟练使用PowerDesigner构建数据库模型理解前端工程的基本架构和执行流程 完成前端工程企业模块开发 多租户SaaS平台的数据库方案 多租户是什么 多租户技术&#xff08;Multi-TenancyTechnology&a…

PCI设备与UIO驱动

随着网络的高速发展,对网络的性能要求也越来越高,DPDK框架是目前的一种加速网络IO的解决方案之一,也是最为流行的一套方案。DPDK通过bypass内核协议栈与内核驱动,将驱动的工作从内核态移至用户态,并利用polling mode的线程工作模式加速网络I/O使得网络IO性能出现大幅度的增…

xml文件报错 ORA-00907: 缺失右括号

原来的sql 更改之后 加一个select * from &#xff08;&#xff09;

一文理解登录鉴权(Cookie、Session、Jwt、CAS、SSO)

1 前言 登录鉴权是任何一个网站都无法绕开的部分&#xff0c;当系统要正式上线前都会要求接入统一登陆系统&#xff0c;一方面能够让网站只允许合法的用户访问&#xff0c;另一方面&#xff0c;当用户在网站上进行操作时也需要识别操作的用户&#xff0c;用作后期的操作审计。…

嵌入式开发学习之STM32F407点亮LED及J-Link下载(二)

嵌入式开发学习之STM32F407点亮LED及J-Link下载&#xff08;二&#xff09; 开发涉及工具控制端口配置端口的设定与确认端口配置方法实现点亮LED程序下载与仿真 有工程实例&#xff0c;链接在最底部。 开发涉及工具 开发环境&#xff08;IDE&#xff09;&#xff1a;IAR-ARM8…

力扣每日一题46:全排列

题目描述&#xff1a; 给定一个不含重复数字的数组 nums &#xff0c;返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3] 输出&#xff1a;[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]示例 2&#xff1a; …

Tuxera NTFS2024最新永久版下载和安装

要使用Tuxera NTFS for Mac&#xff0c;你需要先下载和安装Tuxera NTFS for Mac驱动器&#xff0c;然后按照以下步骤操作&#xff1a; 1、下载和安装Tuxera NTFS for Mac 免费下载Tuxera NTFS for Mac驱动器的最新版本。下载完成后&#xff0c;双击DMG文件并按照提示安装即可…

云计算:shell脚本

shell脚本&#xff0c;会极大减少重复性工作&#xff0c;缩短很大时间。 脚本每个人都可以不一样&#xff0c;只要实现就可以。 注意&#xff1a;要多思考&#xff0c;把思路锻炼好。以后就可以写各种程序。 shell语言 学完shell之后&#xff0c;对Linux理解更深刻&#xff…

IDEA 修改插件安装位置

不说假话&#xff0c;一定要看到最后&#xff0c;不然你以为我为什么要自己总结&#xff01;&#xff01;&#xff01; IDEA 修改插件安装位置 前言步骤 前言 IDEA 默认的配置文件均安装在C盘&#xff0c;使用时间长会生成很多文件&#xff0c;这些文件会占用挤兑C盘空间&…

如何使用前端模块化开发?

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

vue中引入jquery解决跨域问题

1、vue 工程文件 package.json 中 引入 “dependencies”: { “jquery”:“^2.2.4” }, 2、控制台执行命令&#xff0c;当前工程文件夹下 cnpm install 3、修改的vue文件中 加入 import $ from ‘jquery’ 4、调用 ajax请求 $.ajax({url:http://192.168.0.10:9099/strutsJspA…

Vue Router - 路由的使用、两种切换方式、两种传参方式、嵌套方式

目录 一、Vue Router 1.1、下载 1.2、基本使用 a&#xff09;引入 vue-router.js&#xff08;注意&#xff1a;要在 Vue.js 之后引入&#xff09;. b&#xff09;创建好路由规则 c&#xff09;注册到 Vue 实例中 d&#xff09;展示路由组件 1.3、切换路由的两种方式 1.…