1. 初始化列表的实现

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

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

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

【实例分析】initializer_list<T>初体验

#include <iostream>
#include <vector>
#include <map>
#include <complex>
using namespace std; //编译选项:g++ -std=c++11 test1.cpp -fno-elide-constructors class Foo
{
public:
Foo(int)
{
cout << "Foo(int)"<< endl;
} Foo(int, int)
{
cout << "Foo(int, int)"<< endl;
} Foo(const Foo& f)
{
cout << "Foo(const Foo& f)"<< endl;
}
}; int main()
{
Foo f1();
Foo f2 = ; //先将调用Foo(int)将123转为Foo对象,再调用拷贝构造函数(后面这步可能被优化)
Foo f3 = {}; //生成initializer_list<int>,然后分解元素后,由于列表中只有1个元素,所以将其传给Foo(int)
Foo f4 = {, }; //生成initializer_list<int>,然后分解元素后,由于列表中有两个元素,所以将其传给Foo(int, int) //编译器会为以下花括号形成一个initializer_list<string>,背后有个array<string,6>
//调用vector<string>的构造函数时,编译器会找到一个接受initializer_list<string>
//的重载的构造函数。所有的容器均有这样的构造函数。在这个构造函数里会利用
//initializer_list<string>来初始化。
vector<string> city{"Berlin", "New York", "London", "Cairo","Tokyo", "Cologne"}; //编译器会为以下花括号形成一个initializer_list<double>,背后有个array<double,2>。
//调用complex<double>的构造函数时,array内的2个元素被分解并传给
//Comlex<double>(double,double)这个带有两个参数的构造函数。因为comlex<double>并无
//任何接受initializer_list的构造函数。
complex<double> c{4.0, 3.0}; //等价于c(4.0, 3.0) return ;
}

2. initializer_list<T>模板

//initializer_list<T>源码分析

#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中! constexpr initializer_list() : _M_array(), _M_len(){} // empty list,无参构造函数 //size()函数,用于获取元素的个数
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;
}
};

(1)initializer_list是一个轻量级的容器类型,内部定义了iterator等容器必需的概念,本质上是一个迭代器

(2)对于std:: initializer_list<T>而言,它可以接收任意长度的初始化列表,但要求元素必须是同种类型(T或可转换为T)。

(3)它有3个成员函数:size()、begin()和end()

(4)拥有一个无参构造函数,可以被直接实例化,此时将得到一个空的列表。之后可以进行赋值操作,如initializer_list<int> list; list={1,2,3,4,5};

(5)initializer_list<T>在进行复制或赋值时,它内部将保存着列表的地址保存在_M_array中,它进行的是浅拷贝,并不真正复制每个元素,因此效率很高。

【编程实验】打印初始化列表的每个元素

#include <iostream>

//打印初始化列表的每个元素
void print(std::initializer_list<int> vals)
{
//遍历列表中的每个元素
for(auto p = vals.begin(); p!=vals.end(); ++p){
std::cout << *p << " ";
} std::cout << std::endl;
} //std::initializer_list<T>的浅拷贝。以下的返回值应改为std
//以下的返回值应改为std::vector<int>类型,而不是std::initializer_list<int>类型。
std::initializer_list<int> func(void)
{
int a = ;
int b = ; return {a, b}; //编译器看到{a, b}时,会做好一个array<int,2>对象(其生命
//期直至func结束),然后再产生一个initializer_list<int>
//临时对象,由于initializer_list<int>采用的是浅拷贝,当
//函数返回后array<int,2>会被释放,所以无法获取到列表中的元素!
} int main()
{
print({,,,,,,,,,}); print(func()); return ;
}
/*测试结果:
e:\Study\C++11\7>g++ -std=c++11 test1.cpp
e:\Study\C++11\7>a.exe
1 2 3 4 5 6 7 8 9 10
*/

3. 让自定义的类可以接受任意长度初始化列表

(1)自定义类中重载一个可接受initializer_list<T>类型的构造函数

(2)在该构造函数中,遍历列表元素并赋值给相应的字段。

【编程实验】自定义类的初始化列表

#include <iostream>
#include <map> using namespace std; class Foo
{
public:
Foo(int a, int b)
{
cout << "Foo(int a, int b)" << endl;
} Foo(initializer_list<int> list)
{
cout << "Foo(initializer_list<int> list) : "; for(auto i : list){
cout <<i<< " ";
} cout << endl;
} }; class FooMap
{
std::map<int, int> content;
using pair_t = std::map<int, int>::value_type;
public:
FooMap(std::initializer_list<pair_t> list)
{
for(auto it = list.begin(); it!=list.end(); ++it){
content.insert(*it); std::cout << "{" << (*it).first <<"," <<(*it).second <<"}" << " ";
} std::cout << std::endl;
}
}; int main()
{
Foo f1(, ); //Foo(int a, int b), a = 77, b = 5; //注意:由于定义了Foo(initializer_list<int> list)函数,以下3种方
//式的初始化都会将{...}作为一个整体传递给该函数。如果没有定义该函
//数,则由于该类是个非聚合类用{}初始化时,会调用构造函数来初始化。
//但由于Foo类不存在3个参数的构造函数,所以f3那行会编译失败!
Foo f2{, }; //Foo(initializer_list<int> list)
Foo f3{, , }; //Foo(initializer_list<int> list)
Foo f4 = {, }; //Foo(initializer_list<int> list) FooMap fm = {{,}, {,},{,}}; return ;
}
/*测试结果:
e:\Study\C++11\7>g++ -std=c++11 test2.cpp
e:\Study\C++11\7>a.exe
Foo(int a, int b)
Foo(initializer_list<int> list) : 77 5
Foo(initializer_list<int> list) : 77 5 42
Foo(initializer_list<int> list) : 77 5
{1,2} {3,4} {5,6}
*/

第7课 列表初始化(2)_分析initializer_list<T>的实现的更多相关文章

  1. 第8课 列表初始化(3)_防止类型收窄、explicit关键字

    1. 防止类型收窄 (1)类型收窄:指的是导致数据内容发生变化或者精度丢失的隐式类型转换. (2)类型收窄的几种情况: ①从浮点数隐式转换为整型数,如int i=2.2; ②从高精度浮点数隐式转换为低 ...

  2. 列表初始化 分析initializer_list<T>的实现

    列表初始化(1)_统一初始化 1. 统一初始化(Uniform Initialization) (1)在C++11之前,很多程序员特别是初学者对如何初始化一个变量或对象的问题很容易出现困惑.因为可以用 ...

  3. initializer_list 列表初始化

    initializer_list 列表初始化 用花括号初始化器列表初始化一个对象,其中对应构造函数接受一个 std::initializer_list 参数. #include <iostrea ...

  4. kube-scheduler源码分析(1)-初始化与启动分析

    kube-scheduler源码分析(1)-初始化与启动分析 kube-scheduler简介 kube-scheduler组件是kubernetes中的核心组件之一,主要负责pod资源对象的调度工作 ...

  5. C++11 列表初始化

    在我们实际编程中,我们经常会碰到变量初始化的问题,对于不同的变量初始化的手段多种多样,比如说对于一个数组我们可以使用 int arr[] = {1,2,3}的方式初始化,又比如对于一个简单的结构体: ...

  6. 大括号之谜:C++的列表初始化语法解析

    有朋友在使用std::array时发现一个奇怪的问题:当元素类型是复合类型时,编译通不过. struct S { int x; int y; }; int main() { int a1[3]{1, ...

  7. k8s replicaset controller分析(1)-初始化与启动分析

    replicaset controller分析 replicaset controller简介 replicaset controller是kube-controller-manager组件中众多控制 ...

  8. k8s client-go源码分析 informer源码分析(2)-初始化与启动分析

    k8s client-go源码分析 informer源码分析(2)-初始化与启动分析 前面一篇文章对k8s informer做了概要分析,本篇文章将对informer的初始化与启动进行分析. info ...

  9. Redis压缩列表原理与应用分析

    摘要 Redis是一款著名的key-value内存数据库软件,同时也是一款卓越的数据结构服务软件.它支持字符串.列表.哈希表.集合.有序集合五种数据结构类型,同时每种数据结构类型针对不同的应用场景又支 ...

随机推荐

  1. Singer 学习六 运行&&开发taps、targets (一 taps 运行说明)

    文章内容来来自官方github 说明: singer大部分的taps && targets 是用python编写的,所以内容里面的代码也是使用python 编写 使用python运行s ...

  2. 使用 Prometheus 监控minio 性能指标信息&&docker-compose 方式运行

    minio server 已经支持Prometheus 了,对于系统的运行参数我们可以方便.实时的查看 运行使用docker-compose docker-compose 文件 version: &q ...

  3. 利用pipeline批量插入数据到redis

    在推荐系统中,推荐候选集格式一般是,itemid itemid_list.要把itemid作为key,推荐列表作为value批量插入到redis. 比如文件cf.data为: cf_763500210 ...

  4. 修改ORA-28001 口令已经失效问题

    Oracle默认密码的有效时间为180天,当超过180天时,将出现如下错误 解决方法: 使用Oracle SQL Developer登录Oralce, 以as sysdba登录 登录后,执行 sele ...

  5. 让shell脚本中的echo输出带颜色

    转载自:http://www.linuxidc.com/Linux/2014-12/110463.htm 让echo输出带颜色的方法有两种 1.在shell脚本中定义颜色变量,使用echo -e调用变 ...

  6. 简单说说 Java 的 JVM 内存结构

    问:简单说说 Java 的 JVM 内存结构分为哪几个部分? 答:JVM 内存共分为虚拟机栈.堆.方法区.程序计数器.本地方法栈五个部分,分别解释如下.虚拟机栈:线程私有的,每个方法在执行时会创建一个 ...

  7. How to set up github to work with Visual Studio 2013

    http://michaelcrump.net/setting-up-github-to-work-with-visual-studio-2013-step-by-step/ 1. Create gi ...

  8. OpenEXR的输出机制

    最近在一直在研究OpenEXR这个软件.这个软件的图像输出机制很有意思,特地分享一下学习心得,就当是笔记了. 我的OpenEXR软件版本是2.2.0,系统平台是win7. 安装完毕OpenEXR之后我 ...

  9. php函数总结2

    1.匿名参数 func_get_args //获取参数列表的数组 func_get_arg(num) //数组索引 func_num_args() //获取匿名函数的个数 <?php funct ...

  10. ubuntu-docker入门到放弃(四)容器的导入导出

    上一次我们讲了如何搭建自己私有的镜像管理仓库,实际上我们使用的依然是别人或者公共的image,今天就来说说如何将自己定制化的images上传到自己的私有仓库中,以供符合自己业务场景的项目来使用,如:我 ...