zoukankan      html  css  js  c++  java
  • 第7课 列表初始化(2)_分析initializer_list<T>的实现

    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:StudyC++117>g++ -std=c++11 test1.cpp
    e:StudyC++117>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(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:StudyC++117>g++ -std=c++11 test2.cpp
    e:StudyC++117>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}
    */
  • 相关阅读:
    【NOIP2013提高组T3】货车运输-最大生成树+倍增LCA
    【POJ1986】Distance Queries-LCA算法
    【POJ1986】Distance Queries-LCA算法
    【HDU2874】Connections between cities-LCA算法
    【HDU2874】Connections between cities-LCA算法
    【HDU2586】How far away?-LCA算法模板题
    js ajax调用请求
    js ajax调用请求
    ThinkPHP 3.1.2 视图 2
    ThinkPHP 3.1.2 视图 2
  • 原文地址:https://www.cnblogs.com/5iedu/p/7628091.html
Copyright © 2011-2022 走看看