C++11中跳转initializer_list实现分析

 更新时间:2022年4月4日 19:26  点击:249 作者:浅墨浓香

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(123);
    Foo f2 = 123;   //先将调用Foo(int)将123转为Foo对象,再调用拷贝构造函数(后面这步可能被优化)
    Foo f3 = {123}; //生成initializer_list<int>,然后分解元素后,由于列表中只有1个元素,所以将其传给Foo(int)
    Foo f4 = {123, 321}; //生成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 0;
}

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(0), _M_len(0){} // 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 = 1;
    int b = 2;
    return {a, b}; //编译器看到{a, b}时,会做好一个array<int,2>对象(其生命
                   //期直至func结束),然后再产生一个initializer_list<int>
                   //临时对象,由于initializer_list<int>采用的是浅拷贝,当
                   //函数返回后array<int,2>会被释放,所以无法获取到列表中的元素!
int main()
    print({1,2,3,4,5,6,7,8,9,10});
    print(func());
    return 0;
/*测试结果:
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;
    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(77, 5);     //Foo(int a, int b), a = 77, b = 5;
    //注意:由于定义了Foo(initializer_list<int> list)函数,以下3种方
    //式的初始化都会将{...}作为一个整体传递给该函数。如果没有定义该函
    //数,则由于该类是个非聚合类用{}初始化时,会调用构造函数来初始化。
    //但由于Foo类不存在3个参数的构造函数,所以f3那行会编译失败!
    Foo f2{77, 5};     //Foo(initializer_list<int> list)
    Foo f3{77, 5, 42}; //Foo(initializer_list<int> list)
    Foo f4 = {77, 5};  //Foo(initializer_list<int> list)
    FooMap fm = {{1,2}, {3,4},{5,6}};
    return 0;
}
/*测试结果:
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
{1,2} {3,4} {5,6}
*/

到此这篇关于C++11中跳转initializer_list实现分析的文章就介绍到这了,更多相关C++11 initializer_list内容请搜索猪先飞以前的文章或继续浏览下面的相关文章希望大家以后多多支持猪先飞!

原文出处:https://www.cnblogs.com/5iedu/p/7628091.html

[!--infotagslink--]

相关文章

  • php中登录后跳转回原来要访问的页面实例

    在很多网站用户先访问一个要登录的页面,但当时没有登录后来登录了,等待用户登录成功之后肯定希望返回到上次访问的页面,下面我就来给大家介绍登录后跳转回原来要访问的页...2016-11-25
  • java后台实现js关闭本页面,父页面指定跳转或刷新操作

    这篇文章主要介绍了java后台实现js关闭本页面,父页面指定跳转或刷新操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-11-16
  • 微信小程序 页面跳转传递值几种方法详解

    这篇文章主要介绍了微信小程序 页面跳转传递值几种方法详解的相关资料,需要的朋友可以参考下...2017-01-16
  • 基于JavaScript实现网页倒计时自动跳转代码

    这篇文章主要介绍了基于JavaScript实现网页倒计时自动跳转代码 的相关资料,需要的朋友可以参考下...2015-12-29
  • Nginx根据不同浏览器语言配置页面跳转的方法

    这篇文章主要介绍了Nginx根据不同浏览器语言配置页面跳转的方法,包括一个简体繁体的基本判断方法及实际根据中英文跳转的例子,需要的朋友可以参考下...2016-05-22
  • Pycharm 跳转回之前所在页面的操作

    这篇文章主要介绍了Pycharm 跳转回之前所在页面的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-02-05
  • Vue-Element-Admin集成自己的接口实现登录跳转

    关于这个Vue-element-admin中的流程可能对于新的同学不是很友好,所以本文将结合实例代码,介绍Vue-Element-Admin集成自己的接口实现登录跳转,感兴趣的小伙伴们可以参考一下...2021-06-23
  • IE6中链接A的href为javascript协议时不在当前页面跳转

    切页面时有时用链接A来替代按钮,这样做有几个好处 鼠标放上时默认有手状效果(不用添加cursor:pointer) 可以添加低版本IE都支持的伪类 如果点击时页面要整体刷新,即跳转,这时IE6则不尽人意,如下 复制代码 代码如下: <p>...2014-06-07
  • Vue项目中实现带参跳转功能

    最近做了一个手机端系统,其中遇到了父页面需要携带参数跳转至子页面的问题,现已解决,下面分享一下实现过程,感兴趣的朋友一起看看吧...2021-04-15
  • Vue.js实战之利用vue-router实现跳转页面

    对于单页应用,官方提供了vue-router进行路由跳转的处理,这篇文章主要给大家介绍了Vue.js实战之利用vue-router实现跳转页面的相关资料,需要的朋友可以参考借鉴,下面来一起看看吧。...2017-04-03
  • JavaScript实现通过select标签跳转网页的方法

    这篇文章主要介绍了JavaScript实现通过select标签跳转网页的方法,涉及javascript事件响应及窗口操作相关技巧,需要的朋友可以参考下...2016-10-02
  • PHP利用REFERER根居访问来地址进行页面跳转

    比如,我有一个开发一个黄页源码上转到了脚本之家。之前定了一个演示程序地址: http://www.jb51.net 而现在这个域名需要用来作其它的站,又不杀望原来的演示地址失效怎么办呢。那我就可以利用PHP REFERER来判断来源如果...2013-10-04
  • 解决 VSCode 编写 C++11 代码报红问题分析

    今天在写 C++ 代码的时候用上 C++11 的特性,然后发现 VSCode 虽然可以编译通过,但是会在相应位置报红,这是怎么回事呢?下面小编给大家带来了解决方法,一起看看吧...2021-09-27
  • vue相同路由跳转强制刷新该路由组件操作

    这篇文章主要介绍了vue相同路由跳转强制刷新该路由组件操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-05
  • React Router 如何使用history跳转的实现

    这篇文章主要介绍了React Router 如何使用history跳转的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-04-06
  • php 解决扫描二维码下载跳转问题

    这篇文章主要介绍了php 解决扫描二维码下载跳转问题的相关资料,需要的朋友可以参考下...2017-01-25
  • js实现页面跳转重定向的几种方式

    第一种: 复制代码 代码如下: <script language="javascript"type="text/javascript"> window.location.href="http://shanghepinpai.com"; </script> 第二种: 复制代码 代码如下: <script language="javascript"> aler...2014-05-31
  • PHP定时跳转

    用PHP实现"等待指定的时间,然后再跳转到指定页面". 也就是用php实现和HTML中的 一样的效果。 <? /** @title:PHP定时跳转 @功能:等待指定的时间,然后再跳转到指...2016-11-25
  • ASP.NET中配合JS实现页面计时(定时)自动跳转

    这篇文章主要介绍了ASP.NET中配合JS实现页面计时(定时)自动跳转,本文主要依靠JS实现需求,只是在ASP.NET中实现而已,需要的朋友可以参考下...2021-09-22
  • IIS7 IIS8 http自动跳转到HTTPS(80端口跳转443端口)

    这篇文章主要介绍了IIS7 IIS8 http自动跳转到HTTPS(80端口跳转443端口),需要的朋友可以参考下...2020-08-16