• c++11特性initializer_list


    一、概念

    initializer_list是C++11中提供的一种标准库类型(ps:其实也是一个模板类),用于表示某种使用特定类型的值的数组。

    initializer_list中的值都是常量值,无法修改。

    二、提供的操作(以int型举例)

    申明: initializer_list<int> lst;

    也可以这样 initializer_list<int> lst{1,2,3,4};

    还可以这样 initializer_list<int> lst = {1,2,3,4};

    常见操作:

    lst.size() lst.begin() lst.end()

    三、用途之一

    可以更方便的给vector、 string类型赋初始化值。
     

    //c++98
        vector<int> v;
        v.push_back(1);
        v.push_back(2);
        v.push_back(3);
        v.push_back(4);
     
        //c++11,感谢c++11
        vector<int> v2 = { 1, 2, 3, 4 };
     

    四、用途之二

      当一个函数的入参可能有多个参数时,可将入参定义为initializer_list,这样就灵活多了。

    #include <iostream>
    #include <vector>
     
    using namespace std;
     
    class MyNumber
    {
    public:
        MyNumber(const initializer_list<int> &v) {
            for (auto itm : v) {
                mVec.push_back(itm);
            }
        }
     
        void print() 
        {
            for (auto itm : mVec) {
                    cout << itm << " ";
                }
            cout<<endl;
        }
        
    private:
        vector<int> mVec;
    };
     
    int main()
    {
        MyNumber m({1, 2, 3, 4}); 
        
        m.print();  
        
        return 0;
    }
    root@ubuntu:~/c++# g++ -std=c++11  init.cpp -o init
    root@ubuntu:~/c++# ./init
    1 2 3 4 
    #include <iostream>
    #include <vector>
     
    class MyNumber
    {
    public:
        MyNumber (){
    std::vector<int> mvec = {1, 2};
    mVec = mvec;
    std::cout<<"进入构造函数1"<<std::endl;
        }
        MyNumber(const std::initializer_list <int> &v) {
            for (auto itm : v) {
                mVec.push_back(itm);
            }
        }
     
        void print() {
        for (auto itm : mVec) {
            std::cout << itm << " ";
        }
    std::cout<<std::endl;
        }
    private:
        std::vector<int> mVec;
    };
     
    int main()
    {   MyNumber n;
        //n = { 1, 2, 3, 4 };//这句会调用构造函数2
        n.print();
        return 0;
    }
    root@ubuntu:~/c++# g++ -std=c++11  init2.cpp -o init
    root@ubuntu:~/c++# ./init 
    进入构造函数1
    1 2 
    #include <iostream>
    #include <vector>
     
    class MyNumber
    {
    public:
        MyNumber (){
    std::vector<int> mvec = {1, 2};
    mVec = mvec;
    std::cout<<"construct MyNumber ()"<<std::endl;
        }
        MyNumber(const std::initializer_list <int> &v) {
            std::cout<<"construct MyNumber (const std::initializer_list)"<<std::endl;
            for (auto itm : v) {
                mVec.push_back(itm);
            }
        }
     
        void print() {
        for (auto itm : mVec) {
            std::cout << itm << " ";
        }
    std::cout<<std::endl;
        }
    private:
        std::vector<int> mVec;
    };
     
    int main()
    {   MyNumber n;
        n = { 1, 2, 3, 4 };//这句会调用构造函数2
        n.print();
        return 0;
    }
    root@ubuntu:~/c++# g++ -std=c++11  init2.cpp -o init
    root@ubuntu:~/c++# ./init
    construct MyNumber ()
    construct MyNumber (const std::initializer_list)
    1 2 3 4 
    #include <iostream>
    #include <vector>
    #include <map>
    
    // 使用 std::initializer_list<int> 来初始化任意长度的初始化列表
    //stl中的容器是通过使用 std::initializer_list 完成的
    class Foo
    {
    public:
            Foo(std::initializer_list <int>){}
    };
    
    class FooVector
    {
            std::vector<int> content_;
    
    public:
            FooVector(std::initializer_list<int> list)//initializer_list 负责接收初始化列表
            {
                    for (auto it = list.begin(); it != list.end(); ++it)
                    {
                            content_.push_back(*it);
                    }
            }
    };
    
    
    //map 是以 pair形式插入的。map中的元素的类型value_type 
    //typedef pair<const Key, Type> value_type;
    
    
    class FooMap
    {
            std::map<int, int> content_;
            using pair_t = std::map<int, int>::value_type;//重新命名类型   typedef
    
    public:
            FooMap(std::initializer_list<pair_t> list)
            {
                    for (auto it = list.begin(); it != list.end(); ++it)
                    {
                            content_.insert(*it);
                    }
            }
    };
    
    //使用 std::initializer_list 给自定义类型做初始化
    void test01()
    {
            Foo foo = { 1,2,3,4,5 };
            FooVector foo1 = { 1, 2, 3, 4, 5 };
            FooMap foo2 = { { 1, 2 }, { 3, 4 }, { 5, 6 } };
    }
    
    //使用 std::initializer_list 传递同类型的数据
    void func(std::initializer_list<int> list)
    {
            std::cout << "size = "<<list.size() << std::endl;
            //对 std::initializer_list 访问只能通过begin() end() 循环遍历
            //迭代器是只读的,无法修改某一个元素,但可以整体赋值
            for (auto it = list.begin(); it != list.end(); it++)
            {
                    std::cout << *it << std::endl;
            }
    }
    
    void test02()
    {
            std::cout << "test02" << std::endl;
            func({});//1个空集合
            func({ 1,2,3 });//传递 { 1,2,3 }
    }
    
    /*
                    std::initializer_list 的内部并不负责保存初始化列表中元素的拷贝,仅仅
                    存储了列表中元素的引用而已,因此需要再持有对象的生存周期之前传递完毕
    */
    
    //错误的使用方法
    std::initializer_list<int> func2(void)
    {
            int a = 1, b = 2;
            return { a,b };//ab 返回时并没有拷贝
    }
    
    //正确的使用
    std::vector<int> func3(void)
    {
            int a = 1, b = 2;
            return { a,b };//ab 返回时并没有拷贝
    }
    
    void test03()
    {
            std::initializer_list<int> myList;
            size_t n = myList.size();
            std::cout  <<" myList.size " << n << "  "  << std::endl;
            myList = { 1,2,3,4,5,6 };
            n = myList.size();
            std::cout  <<" myList.size " << n << "  "  << std::endl;
            myList = { 11,22};
            n = myList.size();
            std::cout  <<" myList.size " << n << "  "  << std::endl;
            std::vector<int> a;
            a = func2();//值时乱码值
            std::cout  <<" func2 return   "  << std::endl;
        for (auto n : a)
        {
                std::cout << n << std::endl;
        }
            a = func3();
            std::cout  <<" func3 return   "  << std::endl;
        for (auto n : a)
        {
                std::cout << n << std::endl;
        }
    
    }
    
    int main(void)
    {
            test01();
            test02();
            test03();
            return 0;
    }
    root@ubuntu:~/c++# g++ -std=c++11  init2.cpp -o init
    root@ubuntu:~/c++# ./init
    test02
    size = 0
    size = 3
    1
    2
    3
     myList.size 0  
     myList.size 6  
     myList.size 2  
     func2 return   
    2
    0
    ----------------不是1,2
    func3 return 1 2

    How to Initialize a map in one line using initialzer_list ?

    同样的,我们也可以用std::initialzer_list<T>初始化一个map:

    std::map<std::string, int> mapOfMarks = {
    {"Riti",2},
    {"Jack",4}
    };


    相对应的,编译器会在内部创建这样的一个对象:

    std::initializer_list<std::pair<const std::string, int> > = {
    {"Riti",2},
    {"Jack",4}
    };
    #include <iostream>
    #include <vector>
    #include <map>
    
    class FooMap
    {
            std::map<int, int> content_;
            using pair_t = std::map<int, int>::value_type;//重新命名类型   typedef
    
    public:
            FooMap(std::initializer_list<pair_t> list)
            {
                    for (auto it = list.begin(); it != list.end(); ++it)
                    {
                            std::cout << it->first << "  " << it->second << std::endl;
                            content_.insert(*it);
                    }
            }
    };
    int main()
    {
        FooMap foo2 = { { 1, 2 }, { 3, 4 }, { 5, 6 } };
        return 0;
    }
    root@ubuntu:~/c++# g++ -std=c++11  init2.cpp -o init
    root@ubuntu:~/c++# ./init 
    1  2
    3  4
    5  6

    最后,对 std::initializer_list 的访问只能通过 begin() 和 end() 进行循环遍历,遍历时取得的迭代器是只读的。因此,无法修改 std::initializer_list 中某一个元素的值,但是可以通过初始化列表的赋值对 std::initializer_list 做整体修改,代码如下:

    std::initializer_list<int> list;
    size_t n = list.size(); // n == 0
    list = { 1, 2, 3, 4, 5 };
    n = list.size(); // n == 5
    list = { 3, 1, 2, 4 };
    n = list.size(); // n == 4

    std::initializer_list 拥有一个无参数的构造函数,因此,它可以直接定义实例,此时将得到一个空的 std::initializer_list。

    之后,我们对 std::initializer_list 进行赋值操作(注意,它只能通过初始化列表赋值),可以发现 std::initializer_list 被改写成了 {1, 2, 3, 4, 5}。

    然后,还可以对它再次赋值, std::initializer_list 被修改成了 {3, 1, 2, 4}。

    看到这里,可能有读者会关心 std::initializer_list 的传递或赋值效率。

    假如 std::initializer_list 在传递或赋值的时候如同 vector 之类的容器一样,把每个元素都复制了一遍,那么使用它传递类对象的时候就要斟酌一下了

    实际上, std::initializer_list 是非常高效的。它的内部并不负责保存初始化列表中元素的拷贝,仅仅存储了列表中元素的引用而已。

    因此,我们不应该像这样使用:

    #include <iostream>
    #include <vector>
     
    std::initializer_list<int> func(void)
    {
                int a = 1, b = 2;
                return { a, b }; // a、 b 在返回时并没有被拷贝
    }
     
    int main()
    {
        std::vector<int> a = func();
        for (auto n : a)
        {
                std::cout << n << std::endl;
        }
    
        return 0;
    }
    root@ubuntu:~/c++# ./init 
    1
    2

    虽然这能够正常通过编译,但却无法传递出我们希望的结果( a、 b 在函数结束时,生存期也结束了,因此,返回的将是不确定的内容)。

    这种情况下最好的做法应该是这样:

    std::vector<int> func(void)
    {
        int a = 1, b = 2;
        return { a, b };
    }

    使用真正的容器,或具有转移 / 拷贝语义的物件来替代 std::initializer_list 返回需要的结果。

    我们应当总是把 std::initializer_list 看做保存对象的引用,并在它持有对象的生存期结束之前完成传递。

    initializer_list的一些操作

    (1)initializer_list< T > lst : 默认初始化,T类型元素的空列表;
    (2)initializer_list< T > lst{a,b,c} : lst的元素数量和初始值一样多,lst的元素是对应初始值的副本,列表中的元素为const
    (3)lst2(lst)或者lst2 = lst:拷贝或者赋值一个initializer_list对象, 拷贝后,原始列表和副本共享元素;
    (4)lst.size():列表中的元素数量;
    (5)lst.begin():返回指向lst中首元素的指针;
    (6)lst.end():返回指向lst中尾元素下一位置的指针。
     

  • 相关阅读:
    C#面试题
    C# 安装部署
    Ribbon菜单栏布局和调整大小
    Dev GridControl使用小结
    C#多线程学习(一) 多线程的相关概念
    PDF转换成图片——11种方案
    C#多线程学习(二) 如何操纵一个线程
    C# 截屏方式
    dev中gridview控件
    (转载) 如何降低project压缩文件的大小? (Quartus II)
  • 原文地址:https://www.cnblogs.com/dream397/p/15074059.html
Copyright © 2020-2023  润新知