目录
1998 年,C++ 标准委员会发布了第一版 C++ 标准,即 C++98 标准,并计划以后每 5 年视实际需要更新一次标准。
所谓标准,即明确 C++ 代码的编写规范,所有的 C++ 程序员都应遵守此标准。
2003 年,C++ 标准委员会发布了第二版 C++ 标准,即 C++03 标准,但由于 C++03 仅修复了一些 C++98 中存在的漏洞,并未修改核心语法,因此人们习惯将这两个标准合称为 C++98/03 标准。
2011 年,C++ 标准委员会发布了第三版 C++ 标准,即 C++11 标准,相比 C++03,C++11 带来了数量可观的变化,其中包含了约 140 个新特性,以及对 C++03 中约 600 个缺陷的修正,这使得 C++11 更像从 C++98/03 中孕育出来的一种新语言。。
C++ 标准委员会一开始是计划在 2007 年发布第三版 C++ 标准,即 C++07 标准,但在 2006 年时,标准委员会认为到 2007 年,甚至到 2008 年,都可能无法发布第三版 C++ 标准,所以干脆将第三版 C++ 标准命名为 C++0x,即计划在二十一世纪的第一个 10 年的某个时间发布,但最终直到 2011 年才发布第三版 C++ 标准。
在 C++98/03 中,对象的初始化方式有很多种,这些不同的初始化方式都有各自的适用范围和作用,没有一种方式可以通用于所有情况。为了统一初始化方式,并且让初始化行为具有确定的效果,C++11 提出了列表初始化的概念。
在 C++98/03 中,对于普通数组和可以直接进行内存拷贝(memcpy)的对象,可以使用列表初始化来初始化数据。
- int arr[5] = { 0, 1, 2, 3, 4 };
-
- struct Point
- {
- int _x;
- int _y;
- } p = { 0, 0 };
在 C++11 中,初始化列表的适用性被大大地增加了,它现在可以适用于任何类型对象的初始化。
注意:在 C++11 中,使用列表初始化时,可以添加等号(=),也可以不添加等号。
- class A
- {
- public:
- A(int i = 0) : _i(i) { }
- private:
- A(const A& a) : _i(a._i) { }
- private:
- int _i;
- };
-
- int main()
- {
- A a1(10);
- A a2 = 10;
- A a3 = { 10 };
- A a4{ 10 };
- return 0;
- }
a3、a4 使用了 C++11 的列表初始化来初始化对象,效果如同 a1 的直接初始化。
至于 a2,10 会通过隐式类型转换调用构造函数
A(int i = 0)构造出一个匿名对象,然后通过这个匿名对象调用拷贝构造函数A(const A& a)构造出 a2,但由于拷贝构造函数是私有的(private),所以编译器会报错。注意:Linux 中的 g++ 编译器会报错,VS 中的编译器则不会报错。
使用 new 操作符创建新对象时也可以使用列表初始化来初始化对象。
- int* p = new int{ 0 };
- int* arr = new int[5]{ 0, 1, 2, 3, 4 };
除了上面所述的内容,列表初始化还可以直接用在函数传参和返回值上。
- #include
- #include
- using namespace std;
-
- class Person
- {
- public:
- Person(int id, string name) : _id(id), _name(name)
- {
- cout << _id << ":" << _name << endl;
- }
- private:
- int _id;
- string _name;
- };
-
- void func1(Person p) { }
-
- Person func2() { return { 2, "李四" }; }
-
- int main()
- {
- func1({ 1, "张三" }); // 1:张三
- Person p = func2(); // 2:李四
- return 0;
- }
在 C++11 中,列表初始化的使用范围被大大地增加了,但一些模糊的概念也随之而来。
- #include
- using namespace std;
-
- struct T1
- {
- int _x;
- int _y;
- } t1{ 520, 520 };
-
- struct T2
- {
- int _x;
- int _y;
-
- T2(int, int) : _x(1314), _y(1314) { }
- } t2{ 520, 520 };
-
- int main()
- {
- cout << t1._x << ", " << t1._y << endl; // 520, 520
- cout << t2._x << ", " << t2._y << endl; // 1314, 1314
- return 0;
- }
在上面的程序中,t1 和 t2 都使用相同的列表初始化来初始化对象,但输出的结果却不同。因为对于聚合类型的对象 t1,它可以直接使用列表初始化来初始化对象;对于非聚合类型的对象 t2,它是基于构造函数使用列表初始化来初始化对象。
普通数组可以看作是一个聚合类型。
满足以下条件的类(class、struct、union)可以看作是一个聚合类型:
无基类、无虚函数以及无用户自定义的构造函数。
无 private 或 protected 的普通数据成员(即非静态数据成员)。
- struct T1
- {
- int _x;
- int _y;
- private: // 或者 protected
- int _z;
- } t1{ 1, 2, 3 }; // error(类中有私有成员,无法使用列表初始化进行初始化)
-
- struct T2
- {
- int _x;
- int _y;
- protected: // 或者 protected
- static int _z;
- } t2{ 1, 2 }; // ok
-
- int T2::_z = 3; // 注意:静态数据成员 _z 不能使用列表初始化进行初始化
类中不能有 {} 和 = 直接初始化的非静态数据成员(即就地初始化)。
- struct T3
- {
- int _x = 1;
- int _y{ 2 };
- } t3{ 0, 0 }; // error(C++11)
注意:从 C++14 开始,也可以使用列表初始化来初始化类中使用 {} 和 = 初始化过的非静态数据成员。
聚合类型的定义并非递归的,即当一个类的非静态数据成员是非聚合类型时,这个类也可能是聚合类型。
- struct T1
- {
- int _x;
- int _y;
- private:
- int _z;
- public:
- T1() : _x(1), _y(2), _z(3) { }
- };
-
- struct T2
- {
- T1 _t1;
- double _d;
- };
-
- int main()
- {
- T2 t2{ {}, 3.14 };
- return 0;
- }
可以看到,T1 是非聚合类型,因为它有一个 private 的非静态数据成员,但 T2 依然是一个聚合类型,可以直接使用列表初始化来初始化对象 t2。
注意:使用列表初始化来初始化 t2 的非聚合类型成员 _t1 时,可以直接写一对空的大括号 {},这相当于调用 _t1 的默认构造函数。

当编译器看到 { t1, t2, ..., tn } 时,便会生成一个 initializer_list
- #include
- using namespace std;
-
- int main()
- {
- auto il = { 10, 20, 30 };
- cout << typeid(il).name() << endl; // class std::initializer_list
- return 0;
- }
对于聚合类型,编译器会将 array
对于非聚合类型,如果该类存在一个接收 initializer_list
- #include
- #include
- using namespace std;
-
- class Test
- {
- public:
- Test(int) { cout << "Test(int)" << endl; }
-
- Test(int, int) { cout << "Test(int, int)" << endl; }
- };
-
- int main()
- {
- // vector (initializer_list
il, - // const allocator_type& alloc = allocator_type());
- vector<int> v{ 0, 1, 2, 3, 4 };
- for (const auto& e : v)
- {
- cout << e << " ";
- }
- // 0 1 2 3 4
- cout << endl;
-
- Test t1{ 1 }; // Test(int)
- Test t2{ 1, 2 }; // Test(int, int)
- return 0;
- }
- #include
-
- 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 中!
-
- public:
- // 无参的构造函数
- constexpr initializer_list() : _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;
- }
- };
让模拟实现的 vector 也支持列表初始化:
- namespace yzz
- {
- template<class T>
- class vector
- {
- public:
- typedef T* iterator;
- typedef const T* const_iterator;
-
- vector(std::initializer_list
il) : - _start(new T[il.size()]),
- _finish(_start + il.size()),
- _end_of_storage(_finish)
- {
- iterator v_it = _start;
- typename std::initializer_list
::iterator il_it = il.begin(); - while (il_it != il.end())
- {
- *v_it++ = *il_it++;
- }
- }
- // ... ...
- private:
- iterator _start;
- iterator _finish;
- iterator _end_of_storage;
- };
- }