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中尾元素下一位置的指针。
 

 

posted on 2021-07-29 10:53  tycoon3  阅读(231)  评论(0编辑  收藏  举报

导航