重庆巴南网站制作,医院网站建设费用,佛山网站维护,云南装饰公司做网站目录
一、C11 简介
二、列表初始化
2.1 - 统一初始化
2.2 - 列表初始化的使用细节
2.2.1 - 聚合类型的定义
2.2.2 - 注意事项
2.3 - initializer_list
2.3.1 - 基本使用
2.3.2 - 源码剖析 一、C11 简介 1998 年#xff0c;C 标准委员会发布了第一版 C 标准#xff0…目录
一、C11 简介
二、列表初始化
2.1 - 统一初始化
2.2 - 列表初始化的使用细节
2.2.1 - 聚合类型的定义
2.2.2 - 注意事项
2.3 - initializer_list
2.3.1 - 基本使用
2.3.2 - 源码剖析 一、C11 简介 1998 年C 标准委员会发布了第一版 C 标准即 C98 标准并计划以后每 5 年视实际需要更新一次标准。 所谓标准即明确 C 代码的编写规范所有的 C 程序员都应遵守此标准。 2003 年C 标准委员会发布了第二版 C 标准即 C03 标准但由于 C03 仅修复了一些 C98 中存在的漏洞并未修改核心语法因此人们习惯将这两个标准合称为 C98/03 标准。 2011 年C 标准委员会发布了第三版 C 标准即 C11 标准相比 C03C11 带来了数量可观的变化其中包含了约 140 个新特性以及对 C03 中约 600 个缺陷的修正这使得 C11 更像从 C98/03 中孕育出来的一种新语言。。 C 标准委员会一开始是计划在 2007 年发布第三版 C 标准即 C07 标准但在 2006 年时标准委员会认为到 2007 年甚至到 2008 年都可能无法发布第三版 C 标准所以干脆将第三版 C 标准命名为 C0x即计划在二十一世纪的第一个 10 年的某个时间发布但最终直到 2011 年才发布第三版 C 标准。 二、列表初始化
在 C98/03 中对象的初始化方式有很多种这些不同的初始化方式都有各自的适用范围和作用没有一种方式可以通用于所有情况。为了统一初始化方式并且让初始化行为具有确定的效果C11 提出了列表初始化的概念。 2.1 - 统一初始化
在 C98/03 中对于普通数组和可以直接进行内存拷贝memcpy的对象可以使用列表初始化来初始化数据。
int arr[5] { 0, 1, 2, 3, 4 };
struct Point
{int _x;int _y;
} p { 0, 0 };
在 C11 中初始化列表的适用性被大大地增加了它现在可以适用于任何类型对象的初始化。
注意在 C11 中使用列表初始化时可以添加等号也可以不添加等号。
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 使用了 C11 的列表初始化来初始化对象效果如同 a1 的直接初始化。 至于 a210 会通过隐式类型转换调用构造函数 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 iostream
#include string
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;
} 2.2 - 列表初始化的使用细节
在 C11 中列表初始化的使用范围被大大地增加了但一些模糊的概念也随之而来。
#include iostream
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, 520cout t2._x , t2._y endl; // 1314, 1314return 0;
}
在上面的程序中t1 和 t2 都使用相同的列表初始化来初始化对象但输出的结果却不同。因为对于聚合类型的对象 t1它可以直接使用列表初始化来初始化对象对于非聚合类型的对象 t2它是基于构造函数使用列表初始化来初始化对象。 2.2.1 - 聚合类型的定义 普通数组可以看作是一个聚合类型。 满足以下条件的类class、struct、union可以看作是一个聚合类型 无基类、无虚函数以及无用户自定义的构造函数。 无 private 或 protected 的普通数据成员即非静态数据成员。 struct T1
{int _x;int _y;
private: // 或者 protectedint _z;
} t1{ 1, 2, 3 }; // error类中有私有成员无法使用列表初始化进行初始化
struct T2
{int _x;int _y;
protected: // 或者 protectedstatic int _z;
} t2{ 1, 2 }; // ok
int T2::_z 3; // 注意静态数据成员 _z 不能使用列表初始化进行初始化 类中不能有 {} 和 直接初始化的非静态数据成员即就地初始化。 struct T3
{int _x 1;int _y{ 2 };
} t3{ 0, 0 }; // errorC11 注意从 C14 开始也可以使用列表初始化来初始化类中使用 {} 和 初始化过的非静态数据成员。 2.2.2 - 注意事项
聚合类型的定义并非递归的即当一个类的非静态数据成员是非聚合类型时这个类也可能是聚合类型。
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 的默认构造函数。 2.3 - initializer_list
2.3.1 - 基本使用
当编译器看到 { t1, t2, ..., tn } 时便会生成一个 initializer_listT 类型的对象其中 T 为元素的类型它关联到一个 arrayT, n。
#include iostream
using namespace std;
int main()
{auto il { 10, 20, 30 };cout typeid(il).name() endl; // class std::initializer_listintreturn 0;
}
对于聚合类型编译器会将 arrayT, n 内的元素逐一分解并赋值给被初始化的对象这相当于为该对象每个字段分别赋值。
对于非聚合类型如果该类存在一个接收 initializer_listT 类型的构造函数则初始化时会将 initializer_listT 对象作为一个整体传给构造函数如果该类不存在这样的构造函数则 arrayT, n 内的元素会被编译器分解并传给相应的能接收这些参数的构造函数比如列表中有 2 个元素就传给带 2 个参数的构造函数有 3 个元素就传给带 3 个参数的构造函数依次类推。
#include iostream
#include vector
using namespace std;
class Test
{
public:Test(int) { cout Test(int) endl; }
Test(int, int) { cout Test(int, int) endl; }
};
int main()
{// vector (initializer_listvalue_type il, // const allocator_type alloc allocator_type());vectorint v{ 0, 1, 2, 3, 4 };for (const auto e : v){cout e ;}// 0 1 2 3 4cout endl;Test t1{ 1 }; // Test(int)Test t2{ 1, 2 }; // Test(int, int)return 0;
} 2.3.2 - 源码剖析
#include iostream
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
{templateclass Tclass vector{public:typedef T* iterator;typedef const T* const_iterator;
vector(std::initializer_listT il) :_start(new T[il.size()]),_finish(_start il.size()),_end_of_storage(_finish){iterator v_it _start;typename std::initializer_listT::iterator il_it il.begin();while (il_it ! il.end()){*v_it *il_it;}}// ... ...private:iterator _start;iterator _finish;iterator _end_of_storage;};
}