C++initializer_list详解

目录

一、背景

二、initializer_list 基础

三、initializer_list 的工作原理

四、 使用 initializer_list 的优缺点

4.1、优点

4.2、缺点

五、initializer_list 的高级用法

 六、总结


一、背景

        initializer_list 是一种C++11新的类型特性,它允许我们以统一的方式初始化对象。它是一个代表数组的轻量级包装器,通常用于构造函数和函数参数中,以允许传递一个初始化元素列表。

二、initializer_list 基础

        initializer_list是C++11提供的新类型,定义在头文件中。 std::initializer_list 是定义在 <initializer_list> 头文件中的一个模板类。它可以用于接收花括号初始化列表(例如 {1, 2, 3})作为参数。

例如:

#include <initializer_list>
#include <iostream>

void print(std::initializer_list<int> ilist) {
    for (auto elem : ilist) {
        std::cout << elem << ' ';
    }
    std::cout << std::endl;
}

int main() {
    print({1, 2, 3, 4, 5}); // 输出:1 2 3 4 5
    return 0;
}

        用于表示某种特定类型的值的数组,和vector一样,initializer_list也是一种模板类型。

template< class T >class initializer_list;

        要介绍initializer_list的使用,有必要先谈一谈列表初始化。

三、initializer_list 的工作原理

        当使用花括号初始化语法时,编译器会生成一个 std::initializer_list 对象。这个对象实际上包含了两个指针,一个指向数组的开始,另一个指向数组的结束。因此,它的大小相对固定,通常是两个指针的大小。 

template<class E>
class initializer_list {
public:
    // 类型定义
    typedef E value_type;
    typedef const E& reference;
    typedef const E& const_reference;
    typedef size_t size_type;
    typedef const E* iterator;
    typedef const E* const_iterator;

    // 构造函数
    constexpr initializer_list() noexcept;

    // 获得数组大小
    constexpr size_t size() const noexcept;

    // 迭代器
    constexpr const E* begin() const noexcept;
    constexpr const E* end() const noexcept;
};

        C++11扩大了初始化列表的适用范围,使其可用于所有内置类型和用户定义的类型。无论是初始化对象还是某些时候为对象赋新值,都可以使用这样一组由花括号括起来的初始值了。使用初始化列表时,可添加=,也可不添加。

         //定义一个变量并初始化int units_sold=0;

int units_sold(0);

int units_sold={0}; //列表初始化int units_sold{0}; //列表初始化

        当初始化列表用于内置类型的变量时,这种初始化形式有一个重要特点:如果我们使用列表初始化值存在丢失信息的风险,则编译器将报错:

long double ld=3.1415926536;

        int a={ld},b={ld}; //错误:转换未执行,因为存在丢失信息的风险int c(ld),d=ld; //正确:转换执行,且确实丢失了部分值

列表初始化就谈到这里,接下来介绍initializer_list的使用 

它提供的操作如下:

initializer_list<T> lst;

//默认初始化;T类型元素的空列表

initializer_list<T> lst{a,b,c...};

//lst的元素数量和初始值一样多;lst的元素是对应初始值的副本

lst2(lst)

lst2=lst

//拷贝或赋值一个initializer_list对象不会拷贝列表中的元素;拷贝后,原始列表和副本元素共享

lst.size() //列表中的元素数量

lst.begin() //返回指向lst中首元素的指针

lst.end() //返回指向lst中尾元素下一位置的指针

        需要注意的是,initializer_list对象中的元素永远是常量值,我们无法改变initializer_list对象中元素的值。并且,拷贝或赋值一个initializer_list对象不会拷贝列表中的元素,其实只是引用而已,原始列表和副本共享元素。

        和使用vector一样,我们也可以使用迭代器访问initializer_list里的元素

void error_msg(initializer_list<string> il)

{

for(auto beg=il.begin();beg!=il.end();++beg)

cout<<*beg<<" ";

cout<<endl;

}

        如果想向initializer_list形参中传递一个值的序列,则必须把序列放在一对花括号内:

//expected和actual是string对象if(expected != actual)

error_msg({"functionX",expectde,actual});

else

error_msg({"functionX","okay"});

        说了这么多,那initializer_list到底有什么应用呢?

        有了initializer_list之后,对于STL的container的初始化就方便多了,比如以前初始化一个vector需要这样:

std::vector v;

v.push_back(1);

v.push_back(2);

v.push_back(3);

v.push_back(4);

        而现在c++11添加了initializer_list后,我们可以这样初始化

std::vector v = { 1, 2, 3, 4 };

       并且,C++11允许构造函数和其他函数把初始化列表当做参数。

#include <iostream>#include <vector>

class MyNumber
{

public:

MyNumber(const std::initializer_list<int> &v) {

    for (auto itm : v) {

    mVec.push_back(itm);

    }

}

void print() {

    for (auto itm : mVec) {

    std::cout << itm << " ";

    }

}

private:

std::vector<int> mVec;

};


int main()
{
    MyNumber m = { 1, 2, 3, 4 };
    m.print(); // 1 2 3 4

    return 0;

}

四、 使用 initializer_list 的优缺点

4.1、优点

  • (1).统一初始化语法:使用 initializer_list,可以为不同的容器和对象类型提供统一的初始化语法。
  • (2).简化构造函数重载:可以用一个接受 initializer_list 参数的构造函数替代多个重载版本。
  • (3).支持范围 for 循环:initializer_list 支持基于范围的 for 循环,使得遍历元素变得非常简洁。

4.2、缺点

  • (1).只读:initializer_list 中的元素是只读的,你不能修改其中的元素。
  • (2).性能考虑:使用 initializer_list 可能涉及数组的复制,尤其是在传递给构造函数或函数时。
  • (3).生命周期:initializer_list 引用的元素数组的生命周期与 initializer_list 对象的生命周期相同,这可能导致悬垂引用的风险。

五、initializer_list 的高级用法

        initializer_list 还可以用于类模板和自定义类型。例如,你可以为你的容器类提供一个接受 initializer_list 的构造函数:

#include <initializer_list>
#include <vector>
#include <iostream>

class IntContainer {
    std::vector<int> m_data;
public:
    IntContainer(std::initializer_list<int> list) : m_data(list)    void print() const {
        for (auto elem : m_data) {
            std::cout << elem << ' ';
        }
        std::cout << std::endl;
    }
};

int main() {
    IntContainer container = {1, 2, 3, 4, 5};
    container.print(); // 输出:1 2 3 4 5
    return 0;
}

         在这个例子中,IntContainer 类有一个接受 initializer_list 的构造函数,它允许我们使用花括号初始化语法来初始化 IntContainer 对象。

 六、总结

        initializer_list 是 C++11引入的一个强大特性,它为统一初始化提供了支持,简化了构造函数重载,并使得初始化列表的使用变得更加灵活。但是,在使用 initializer_list 时,需要注意其只读性质和潜在的生命周期问题。理解其工作原理及限制后,你可以有效地在自己的 C++代码中使用 initializer_list,使代码更加清晰和高效。

  • 13
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大王算法

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值