昆明个人网站建设平台,777fj做最好的网站,电子商务类网站建设实训报告,绑定网站C11#xff1a;声明 初始化 初始化{ }初始化initializer_list 声明autodecltypenullptr 初始化
{ }初始化
在C98中#xff0c;允许使用花括号{ }对数组或者结构体元素进行统一的列表初始化。
用{ }初始化数组#xff1a;
int arr[] { 1, 2, 3, 4, 5 };用{ }初始化… C11声明 初始化 初始化{ }初始化initializer_list 声明autodecltypenullptr 初始化
{ }初始化
在C98中允许使用花括号{ }对数组或者结构体元素进行统一的列表初始化。
用{ }初始化数组
int arr[] { 1, 2, 3, 4, 5 };用{ }初始化结构体
struct stu
{char name[20];int age;
};int main()
{stu s1 { Jones, 18 };return 0;
}C11扩大了{ }的适用范围其可以用于所有的内置类型和自定义类型的初始化 C11希望通过这个语法使得所有变量可以以一种统一的方式进行初始化
比如对于内置类型
int i { 1 };
double d { 3.14 };
char c { x };int* pf { nullptr };
int ri { i };我们可以通过大括号初始化整型浮点型指针引用等等。在使用{ }进行初始化时可以省略掉 所以以上代码也可以写为
int i{ 1 };
double d{ 3.14 };
char c{ x };int* pf{ nullptr };
int ri{ i };但是这个语法其实也比较鸡肋因为这个写法完全没有直接int i 1;这样来的直接。
接着就是自定义类型的初始化
struct stu
{char name[20];int age;
};int main()
{stu s{ jack, 18 };string str{ hello world };int arr[5]{ 1, 2, 3, 4, 5 };return 0;
}以上代码中通过直接在变量后面加一对大括号来实现初始化数组和结构体初始化时的也可以省略掉。
在此我要额外辨析一下现有的类的初始化方式
现在有如下日期类
class Date
{
public:Date(int year, int month, int day): _year(year),_month(month),_day(day){}private:int _year;int _month;int _day;
};其含义三个变量表示年月日一个三参数的构造函数初始化这个Date。
我们有如下方式对其初始化
Date d1(2024, 4, 3);
Date d2 { 2024, 4, 3 };
Date d3{ 2024, 4, 3 };请问这三个方式分别是如何初始化一个类的
对于Date d1(2024, 4, 3);其就是最基础的构造函数调用语法也就是直接构造对于Date d2 { 2024, 4, 3 };很多人看到这个写法再想到我们刚才讲的{ }初始化以为这个是C11的新语法其实并不是的。这个写法是多参数构造函数的类型转化也就是说这个写法{ 2024, 4, 3 };就是把三个int类型转换为Date类型。如果你用explicit关键字修饰这个构造函数那么类型转换功能就会被禁止这个写法就会报错对于Date d3{ 2024, 4, 3 };这个写法即使用了{ }而且还省略了这就是C11提供的新语法了当用explicit修饰这个构造函数这个写法依然有效。因为这个写法也是直接调用构造函数而不是进行类型转换
其实整体上来说C11提供{ }的意图在于提供统一的方式来初始化所有类型但是奈何大部分程序员已经习惯了之前的写法{ }既没有带来效率的提高也没有更加人性化的语法设计甚至我感觉int i 1比int i(1);更符合人类的习惯因此这个语法并没有被广泛接受。 initializer_list
initializer_list是一个新的C类型我先为大家创建一个initializer_list类型
auto li { 1, 2, 3, 4 };此时li的类型就是initializer_list这个时候有的人就疑惑了{ 1, 2, 3, 4 }分明是一个整型数组怎么改了个名字就变成新类型了initializer_list翻译为中文就是初始化列表也就是说这是一个用于初始化的工具。
假设现在你有以下数组
int arr[5] { 1, 2, 3, 4 };你要如何用这个数组来初始化一个vector初始化一个list初始化一个set呢
我们好像只能粗暴的遍历数组然后一个一个插入数据
vectorint v;
for (int i 0; i 5; i)
{v.push_back(arr[i]);
}这着实有点麻烦了但是C11后STL的所有容器都增加了新的构造函数可以通过initializer_list来初始化容器
vectorint v({ 1, 2, 3, 4, 5 });以上代码中{ 1, 2, 3, 4, 5 }整体就是一个initializer_list作为参数传给v调用vector的构造函数。
当然我们也可以这样写
vectorint v { 1, 2, 3, 4, 5 };这个写法则是单参数的类型转化因为{ 1, 2, 3, 4, 5 }整体就是一个initializer_list类型的参数。
相同的办法我们还可以初始化map
mapstring, string m { {apple,苹果}, {strawberry,草莓}, {watermelon, 西瓜} };以上代码中最外层的{ }括起来的就是一个initializer_list内部的三个{ }则是三个不同的pairconst char*, const char*不过const char*可以转为string因此最后pairconst char*, const char*会变成pairstring, string。
最外层的initializer_list内部的三个pair会依次插入进map中也就是一次拿多个值初始化map的多个节点。
至此你应该理解了initializer_list就是在类构造时如果我们想要一次性初始化多个节点就把这些节点放进一个initializer_list内部这样就能在构造函数中直接构造好。
initializer_list本质上也是一个容器一个类模板 因此{ 1, 2, 3, 4, 5 }的准确类型应该是initializer_listint。
而initializer_list的底层也非常简单我们看看其仅有的四个接口 一个构造函数constructor一个描述长度的接口size以及迭代器beginend。也就是说initializer_list本质上是一个通过迭代器访问数组的容器。当其它容器通过initializer_list构造自己其实就是通过迭代器遍历那个存储了节点的数组然后把数组元素一个一个插入。
也就是说以下两种情况本质是一样的
initializer_listint lt { 1, 2, 3, 4 };listint l1({ 1, 2, 3, 4 });
listint l2(lt.begin(), lt.end());第一个list通过initializer_list初始化自己第二个list则通过迭代器初始化自己。不过前者更加方便是C11提供的而后者是C98提供的。 声明
auto
在C11中新增了关键字auto其可以自动推导类型
auto i 1;//整型
auto d 3.14;//浮点型
auto p i;此时i就会被自动识别为intd就自动识别为doublep自动识别为int*。
auto的主要作用在于对于有一些类型它的长度太长了我们就可以用auto一笔带过。
比如完整地定义一个迭代器
vectorint v;
vectorint::iterator it v.begin();但是我们可以用auto直接自动识别
vectorint v;
auto it v.begin();在定义迭代器的时候auto的使用还是比较常见的。 decltype
在C11以前有一个关键字typeid其可以识别一个类型并且可以通过name成员函数来输出类型名。
比如这样
int i 0;
int* pi i;cout typeid(i).name() endl;
cout typeid(pi).name() endl;输出结果为
int
int * __ptr64也就是说我们可以通过typeid来检测甚至输出变量类型。
而decltype也是用于识别类型的但是decltype与typeid应用方向不同。 decltype可以检测一个变量的类型并且拿这个类型去声明新的类型 比如这样
int i 0;
decltype(i) x 5;decltype(i)检测出i的类型为int于是decltype(i)整体就变成int从而定义出一个新的变量x。 nullptr
在C11后推出了新的空指针nullptr明明已经有NULL了为啥还需要nullptr? NULL在C语言中表示的是((void*)0)也就是被强制转为void*类型的0。但是在C中NULL就是整数0 比如可以用刚才学的typeid验证一下
cout typeid(NULL).name() endl;输出结果为int这下就石锤了NULL在C中就是int。
这会导致不少问题比如这样
void func(int x)
{cout 参数为整型 endl;
}void func(void* x)
{cout 参数为指针 endl;
}int main()
{func(NULL);return 0;
}以上代码中func函数有两个重载一个是参数为指针一个是参数为整型。我现在就是想传一个空指针去调用指针版本的func。但是最后还是会调用int类型的。
而nullptr不一样nullptr不仅不是整型而且其也不是void*。C给了nullptr一个专属类型nullptr_t。这个类型有一个非常非常大的优势该类型只能转化为其它指针类型不能转化为指针以外的类型。
比如以下代码
int x1 NULL;//正确
int x2 nullptr;//错误因为NULL本质是0其可以转化为很多非指针类型比如intdoublechar。但是nullptr是nullptr_t它只能转化为其他指针。上述代码中我们把nullptr转化为一个int此时编译器会直接报错绝对禁止这个行为。
但是这样是可以的
void* p1 nullptr;
int* p2 nullptr;
char* p3 nullptr;
double* p4 nullptr;可以看到nullptr保证了指针类型的稳定空指针不会被传递到指针以外的类型。因此nullptr在各方面都有足够的优势以更加安全的形式给用户提供空指针。