目录
在2003年C++标准委员会曾经提交了一份技术勘误表(简称TC1),使得C++03这个名字已经取代了C++98称为C++11之前的最新C++标准名称;不过由于TC1主要是对C++98标准中的漏洞进行修复,语言的核心部分则没有改动,因此人们习惯的把两个标准合称为C++98/03标准;从C++0x到C++11,C++标准十年磨一剑,第二个真正意义上的标准姗姗来迟; 相比于C++98/03,C++11则带来了数量可观的变化,其中包含了约140个新特性,以及对C++03标准中约600个缺陷的修正,这使得C++11更像是从C++98/03中孕育出的一种新语言;相比较而言,C++11能更好地用于系统开放和库开发,语法更加泛化和简单化,更加稳定和安全,不仅功能强大,而且能提升程序员的开发效率;
一,列表初始化
列表初始化(List Initialization)是一种 C++11 引入的一种统一的初始化语法,可用来初始化各种类型的对象,包括数组、结构体、类以及容器等;提供了更安全、更一致的初始化方式,能够应用于各种初始化场景;
具体来说,列表初始化使用花括号 {} 将一组值括起来,用逗号分隔各个元素,形成一个初始化列表;注意与类成员初始化列表(如:_a(a))的区别;
{ }初始化
在C++98中,标准允许使用{}对数组或结构体元素进行统一的列表初始化设定;但对一些自定义类型不可这样使用,无法编译,会导致如每次定义vector时,都需要先定义vector,在循环对其赋初始值,非常不方便;
//C环境
/*struct Point
{
int _x, _y;
};*/
int arr[] = {1, 2, 3, 4, 5};
int arr[5] = {0};
Point p = { 1, 2 };
//c++98中不可这样使用,需循环赋值
vector<int> v = {1, 2, 3, 4, 5};
C++11扩大了用大括号{}括起来的列表(初始化)的使用范围,使其可用于所有的内置类型和用户自定义的类型,使用列表初始化时,可添加等号(=),也可不添加;
编译器会对大括号中的元素进行类型检查,不符合要求即报错;
- 元素类型与被初始化对象类型匹配;
- 对自定义类型大括号内的元素个数与被初始化对象构造函数参数个数匹配;
//内置类型
int x = 10;
int x = {10};
int x{10};
int x = 1+2;
int x = {1+2};
int x{1+2};
//数组
int arr[5] = {1,2,3,4,5};
int arr[] = {1,2,3,4,5};
int arr[]{1,2,3,4,5};
//动态数组,C++98不支持
int* parr = new int[5]{1,2,3,4,5};
int* parr = new int[]{1,2,3,4,5};
//标准容器,调用initializer list构造函数
vector<int> v1{ 1, 2, 3, 4, 5 };
vector<int> v2={ 1, 2, 3, 4, 5 };
map<int, int> m{{1,1},{2,2},{3,3},{4,4}};
对于内置类型,列表初始化在某些情况下可能会导致不同的行为,特别是当涉及到类型推导;通常推荐使用直接初始化(如int x = 5)或列表初始化(C++11引入的int x{5});
auto x = {5}; //实际类型为std::initializer_list<int> int x = 5; //直接初始化 int x{5};
优势
- 统一的初始化语法,可用于几乎所有初始化场景;
// 变量初始化
int x{5};
// 动态分配内存
int* p = new int{10};
// 函数返回值
std::vector<int> makeVec() {
return {1, 2, 3}; // 返回列表初始化
}
// 类成员初始化
class Widget {
std::vector<int> data{1, 2, 3}; // 成员初始化
};
- 防止窄化转换(Narrowing Conversion),即不允许可能导致数据丢失的隐式类型转换;
int a = 3.14; // 警告但允许,a=3
int b{3.14}; // 错误!窄化转换从double到int
char c{1000}; // 错误!窄化转换(如果char是8位)
unsigned u{-1}; // 错误!窄化转换
- 避免最令人烦恼的解析(Most Vexing Parse);
class Timer;
// 传统语法可能被解析为函数声明
Timer t(); // 函数声明,返回Timer对象
// 列表初始化明确表示对象初始化
Timer t{}; // 明确初始化Timer对象
使用场景
-
容器初的始化;
//标准容器,调用initializer list构造函数
vector<int> v1{ 1, 2, 3, 4, 5 };
vector<int> v2={ 1, 2, 3, 4, 5 };
map<int, int> m{{1,1},{2,2},{3,3},{4,4}};
-
自定义类型的初始化,会尝试调用匹配的构造函数;
class Point
{
public:
Point(int x=0,int y=0)
:_x(x), _y(y)
{}
private:
int _x, _y;
};
int main()
{
//以下等价,构造函数参数列表与大括号内元素个数一致
Point p(1, 2);
Point p1 = { 1, 2 };
Point p2{ 1, 2 };
}

-
聚合类初始化,即没有用户定义的构造函数、没有私有/受保护的非静态成员等);
//struct或class
struct Aggregate {
int x;
double y;
std::string name;
};
Aggregate a{10, 3.14, "test"}; // 聚合初始化
二,类模板列表初始化
- 要支持初始化列表,需给该类(模板类)添加一个带有initializer_list类型参数的构造函数;
- 所有现代STL容器都支持
initializer_list构造函数,方便实现任意数量参数的函数; initializer_list是轻量级容器,不管理内存,只是底层数组的视图;- 元素是const,不能修改;
- 廉价的拷贝,拷贝操作通常共享底层数据;
- 比普通构造函数优先级更高;
- 所有现代STL容器都支持
- initializer_list是系统自定义的类模板,该类模板中主要有三个方法:
- begin()、end()迭代器;
- 获取区间中元素个数的方法size();
- Initializer list 类型用于访问初始化列表中的值;
- Initializer list 类型对象由编译器从初始化列表声明(用大括号括起来的逗号分隔的元素列表)中自动构造,即{value1,value2,...}会自动构造一个Initializer list对象;
//库定义
template <class _Elem>
class initializer_list
{
public:
...
constexpr initializer_list() noexcept : _First(nullptr), _Last(nullptr) {}
constexpr initializer_list(const _Elem* _First_arg, const _Elem* _Last_arg) noexcept
: _First(_First_arg), _Last(_Last_arg) {}
_NODISCARD constexpr const _Elem* begin() const noexcept {return _First;}
_NODISCARD constexpr const _Elem* end() const noexcept {return _Last;}
_NODISCARD constexpr size_t size() const noexcept {return static_cast<size_t>(_Last - _First);}
private:
const _Elem* _First;
const _Elem* _Last;
};
std::initializer_list<int> li = { 1,2,3,4 };
std::cout << li.size() << std::endl;
for (auto i = li.begin(); i != li.end(); i++)
{
std::cout << *i << std::endl;
}
template<class T>
class Point
{
public:
Point(T x, T y)
:_x(x), _y(y)
{
cout << "Point(int x = 0, int y = 0)" << endl;
}
Point(std::initializer_list<T> li)
{
_x = *(li.begin());
_y = *(li.begin()+1);
cout << "Point(std::initialier_list<T> li)" << endl;
}
private:
T _x, _y;
};
int main()
{
Point<int> p = { 1,2 }; //优先使用Point(std::initializer_list<T> li)
}
库模板初始化列表构造函数的实现
- 调用对应的初始化列表构造函数,然后将初始化列表值循环将元素赋值到对象内;
//vector库模板,初始化列表构造函数
_CONSTEXPR20_CONTAINER vector(initializer_list<_Ty> _Ilist, const _Alloc& _Al = _Alloc())
: _Mypair(_One_then_variadic_args_t{}, _Al)
{
auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal());
_Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2);
//初始化列表赋值
_Range_construct_or_tidy(_Ilist.begin(), _Ilist.end(), random_access_iterator_tag{});
_Proxy._Release();
}
template <class _Iter>
_CONSTEXPR20_CONTAINER void _Range_construct_or_tidy(_Iter _First, _Iter _Last, input_iterator_tag) {
_Tidy_guard<vector> _Guard{ this };
for (; _First != _Last; ++_First) {
emplace_back(*_First); // performance note: emplace_back()'s strong guarantee is unnecessary here
}
_Guard._Target = nullptr;
}
模拟模板初始化列表实现
#include<initializer_list>
template<class T>
class MyVector
{
public:
MyVector(std::initializer_list<T> lt)
:_capacity(lt.size())
,_size(0)
{
_array = new T[_capacity];
for (auto e : lt)
_array[_size++] = e;
}
MyVector<T>& operator=(std::initializer_list<T> lt)
{
delete[] _array;
_capacity = lt.size();
_array = new T[_capacity];
_size = 0;
for (auto e : lt)
_array[_size++] = e;
return *this;
}
private:
T* _array;
size_t _capacity;
size_t _size;
};
int main()
{
//以下三种形式等价
MyVector<int> v1({ 1,2,3 });
MyVector<int> v2 = { 1,2,3 };
MyVector<int> v3{ 1,2,3 };
//调用operator=
v1 = { 3,2,1 };
return 0;
}

本文围绕C++11的列表初始化特性展开。介绍了C++标准的发展,对比C++98/03,C++11有诸多新特性。详细阐述了列表初始化,包括其在C++98和C++11中的使用差异,还介绍了类列表初始化及类模板列表初始化,包含库模板和模拟模板的实现。

4万+

被折叠的 条评论
为什么被折叠?



