Skip to content

Latest commit

 

History

History
141 lines (117 loc) · 4.51 KB

01_一致性初始化.md

File metadata and controls

141 lines (117 loc) · 4.51 KB

在 C++98 中,支持了在类声明中使用等号 "=" 加初始值的方式,来初始化类中静态成员常量。这种声明方式也称之为"就地"声明。

就地声明

在 C++11 中,增加了对普通成员、静态成员进行就地声明的支持,还允许以 {} 的方式进行就地声明。同时支持初始化列表。

示例程序

程序员可以为同一变量既声明就地的列表初始化,又在初始化列表中进行初始化,只不过初始化列表总是看起来"后作用于"非静态成员。也就是说,初始化列表的效果总是优先于就地初始化的。

一致性初始化

C++ 中对变量的初始化方式有多种,为了统一与安全,C++11 中引入了一致性初始化。

一致性初始化示例:

    int i{};
    int *q{};
    double x1{5.3};
    int values[]{1, 2, 3};
    std::vector<int> v{2, 3, 5, 7, 11, 13, 17};

一致性初始化使赋值的类型更加安全:

    int x = 5.3;    // 编译成功,类型截断
    int x{5.3};     // 编译失败, 5.3 是实数

C++11 的一致性初始化是通过 std::initializer_list 来实现的。当检测到 {} 初始化变量时,编译器会将 {} 里面的参数装入一个 std::initializer_list 列表。

std::initializer_list 的设计

    template<typename _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;
    
    private:
        iterator    _M_array;
        size_type   _M_len;
        
        // 提供一个私有构造函数供编译器调用,用于构造初始化列表
        constexpr initializer_list(const_iterator __a, size_type __l) : _M_array(__a), _M_len(__l) {}
        
    public:
        constexpr initializer_list() noexcept : _M_array(0), _M_len(0) {}
        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. std::initializer_list 可以接收任意长度的初始化列表,但要求元素必须是同种类型 T (或可转换为 T)。因为 std::initializer_list 是由一个数组构造的。
  2. std::initializer_list 提供了 3 个成员接口: size()、begin()、end(),而且这些接口都是只读的。因此,无法修改其中某个元素的值,但可以通过初始化列表的赋值对其作整体修改。
  3. std::initializer_list 是高效的。它的内部并不负责保存初始化列表中元素的拷贝,仅仅存储了列表元素的引用而已。也因此要避免如下错误的使用方法:
  /** 错误用法 */
  std::initializer_list<int> func(void)
  {
      int a = 1, b = 2;
      return { a, b };
  }
  /** 正确用法 */
  std::vector<int> func(void)
  {
      int a = 1, b = 2;
      return { a, b };
  }

初始化列表在自定义类及STL容器中的使用

C++11 中, 标准模板类支持列表初始化,其使用 std::initializer_list 这个轻量级的类模板来完成该功能。

自定义类列表初始化:

    class Foo
    {
    public:
        Foo(std::initializer_list<int>) {}
    };
    
    Foo foo = { 1, 2, 3, 4, 5 };    // ok

自定义容器列表初始化:

    class FooVector
    {
        std::vector<int> content_;
    public:
        FooVector(std::initializer_list<int> list)
        {
            for (auto it = list.begin(); it != list.end(); it++)
            {
                content_.push_back(*it);
            }
        }
    };
    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);
            }
        }
    };
    FooVector foo_1 = { 1, 2, 3, 4, 5 };
    FooMap foo_2 = { {1, 2}, {3, 4}, {5, 6} };

作为同类型参数列表传递:

    void func(std::initializer_list<int> l)
    {
        for (auto it = l.begin(); it != l.end(); it++)
        {
            std::cout << *it << std::endl;
        }
    }

    // main
    func({});           // 空集合
    func({1, 2, 3});