用于做网站的软件,个人网站建设营销推广,wordpress主页面,北京网站外包列表初始化、声明、范围for、array容器 一、统一的列表初始化1.1 使用{ }初始化1.2 initializer_list容器 二、声明2.1 auto关键字2.2 decltype关键字2.3 nullptr关键字 三、范围for四、array容器和forward_list容器 一、统一的列表初始化
1.1 使用{ }初始化
在C98中#xf… 列表初始化、声明、范围for、array容器 一、统一的列表初始化1.1 使用{ }初始化1.2 initializer_list容器 二、声明2.1 auto关键字2.2 decltype关键字2.3 nullptr关键字 三、范围for四、array容器和forward_list容器 一、统一的列表初始化
1.1 使用{ }初始化
在C98中标准允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定。比如
struct Person
{string _nameint _age;
};
int main()
{int arr1[] { 1, 2, 3, 4, 5 };int arr2[5] { 0 };Person p { 张三, 21};return 0;
}C11扩大了用大括号括起的列表(初始化列表)的使用范围使其可用于所有的内置类型和用户自定义的类型使用初始化列表时可添加等号()也可不添加。
struct Person
{string _nameint _age;
};
int main()
{//单个元素int a 1;int b { 2 };int c{ 2 };//数组int arr1[]{ 1, 2, 3, 4, 5 };int arr2[5]{ 0 };//自定义结构体Person p { 张三, 21};//调用new时对每个对象初始化int* p1 new int(1);int* p2 new int[3]{ 1, 3, 4 };return 0;
}创建对象时也可以使用列表初始化方式调用构造函数初始化。
class Date
{
public:Date(int year, int month, int day):_year(year), _month(month), _day(day){cout Date(int year, int month, int day) endl;}
private:int _year;int _month;int _day;
};
int main()
{Date d1(2024, 12, 17); // old style// C11支持的列表初始化这里会调用构造函数初始化Date d2 { 2024, 12, 17 };//类似隐式类型转换 优化Date d3{ 2024, 12, 17 };Date* p1 new Date(2024, 12, 17);Date* p2 new Date[3]{ { 2024, 12, 17}, { 2024, 12, 18}, { 2024, 12, 19} };return 0;
}1.2 initializer_list容器
c11里添加了initializer_list容器介绍文档链接initializer_list的介绍
此容器提供的成员函数只有三个还有一个构造函数 int main()
{initializer_listdouble lt { 24, 17, 21 };initializer_listdouble::iterator it lt.begin();while (it ! lt.end()){cout *it ;//24 17 21it;}cout endl;for (auto e : lt){cout e ;//24 17 21}return 0;
}该类型用于访问C初始化列表中的值该列表是 类型的元素列表。这种类型的对象由编译器从初始化列表声明自动构造初始化列表声明是用大括号括起来的逗号分隔元素的列表const T。常量的花括号列表会被编译器识别成initializer_list
int main()
{// the type of il is an initializer_listauto il { 10, 20, 30 };cout typeid(il).name() endl;//class std::initializer_listintreturn 0;
}initializer_list的使用场景
initializer_list一般是作为构造函数的参数C11对STL中的不少容器增加initializer_list作为参数的构造函数这样初始化容器对象时就变得方便了也可以作为operator 的参数这样就可以用大括号赋值
class Date
{
public:Date(int year, int month, int day):_year(year), _month(month), _day(day){cout Date(int year, int month, int day) endl;}
private:int _year;int _month;int _day;
};
int main()
{//vectorvectorint v1 { 1, 2 ,3 ,4, 5 };// 使用大括号对容器赋值{}调用构造函数构造一个vector对象再赋值v1 {10, 20, 30};vectorint v2{ 1, 2, 3, 4, 5 };vectorDate v3 { { 2024, 12, 17}, { 2024, 12, 18}, { 2024, 12, 19} };//listlistint lt1{ 1, 2, 3 };//setsetint s1{ 3, 4, 5, 6, 3 };//mapmapstring, string dict { {apple, 苹果 }, {english, 英语 } };return 0;
}initializer_list的使用示例
对我们以前实现过的vector类型中的构造函数进行改动可以使构造函数还能实现的再简单点直接在初始化列表里把三个成员变量初始化在遍历ls复用push_back依次插入到vector即可
vector(initializer_listT il):_start(nullptr), _finish(nullptr), _endofstoage(nullptr)
{for (auto e : il){push_back(e);}
}注意
最好增加一个以initializer_list作为参数的赋值运算符重载函数以支持直接用列表对容器对象进行赋值但实际也可以不增加。
如下
vectorint v1 { 1, 2 ,3 ,4, 5 };
// 使用大括号对容器赋值{}调用构造函数构造一个vector对象再赋值
v1 {10, 20, 30};对于第二行的赋值操作涉及到了隐式类型转换先使用{}调用构造函数构造一个vector对象再赋值。
二、声明
c11提供了多种简化声明的方式尤其是在使用模板时。
2.1 auto关键字
在C98中auto是一个存储类型的说明符表明变量是局部自动存储类型但是局部域中定义局部的变量默认就是自动存储类型所以auto就没什么价值了。C11中废弃auto原来的用法将其用于实现自动类型腿断。这样要求必须进行显示初始化让编译器将定义对象的类型设置为初始化值的类型。
int main()
{int i 10;//int*auto p i;mapstring, string dict { {sort, 排序}, {insert, 插入} };//mapstring, string::iteratorauto it dict.begin();return 0;
}2.2 decltype关键字
decltype 是 C11 引入的一个关键字用于在编译时推导表达式的类型。它根据给定表达式的类型来确定一个新类型可以在模板编程和类型推导中非常有用
// decltype的一些使用使用场景
templateclass T1, class T2
void F(T1 t1, T2 t2)
{decltype(t1 * t2) ret;cout typeid(ret).name() endl;//double
}
int main()
{const int x 1;double y 2.2;decltype(x * y) ret; // ret的类型是doubledecltype(x) p; // p的类型是int const *cout typeid(ret).name() endl;//doublecout typeid(p).name() endl;//int const *F(1, a);return 0;
}下面来区分下typeid和decltype
typeid(变量名).name()专门用来输出一个变量的类型返回的是一个字符串。帮助我们观察此字符串的类型不能用其去定义变量。decltype将变量的类型声明为表达式指定的类型可以用其去定义变量。
2.3 nullptr关键字
由于C中NULL被定义成字面量0这样就可能回带来一些问题因为0既能指针常量又能表示整形常量。所以出于清晰和安全的角度考虑C11中新增了nullptr用于表示空指针。
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endi如果没有定义宏如果在cplusplus里NULL被定义成0。可以看到NULL可能被定义为字面常量0或者被定义为无类型指针(void*)的常量。不论采取何种定义在使用空值的指针时都不可避免的会遇到一些麻烦比如
void print(int* a){cout int* endl;
}
void print(int a){cout int endl;
}int main(){print(NULL); //intreturn 0;
}程序本意是想通过print(NULL)调用指针版本的print(int *)函数但是由于NULL被定义成0因此与程序的初衷相悖。在C98中字面常量0既可以是一个整形数字也可以是无类型的指针(void)常量但是编译器默认情况下将其看成是一个整形常量如果要将其按照指针方式来使用必须对其进行强转(void *)0。
注意
在使用nullptr表示指针空值时不需要包含头文件因为nullptr是C11作为新关键字引入的。在C11中sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。为了提高代码的健壮性在后续表示指针空值时建议最好使用nullptr。
三、范围for
范围for的底层就是被替换成了迭代器。 范围for的语法 若是在C98中我们要遍历一个数组可以按照以下方式
int main()
{int arr[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };//将数组元素值全部乘以2for (int i 0; i sizeof(arr) / sizeof(arr[0]); i){arr[i] * 2;}//打印数组中的所有元素for (int i 0; i sizeof(arr) / sizeof(arr[0]); i){cout arr[i] ;}cout endl;return 0;
}以上方式也是C语言中所用的遍历数组的方式但对于一个有范围的集合而言循环是多余的有时还容易犯错。
C11中引入了基于范围的for循环for循环后的括号由冒号分为两部分第一部分是范围内用于迭代的变量第二部分则表示被迭代的范围。比如
int main()
{int arr[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };//将数组元素值全部乘以2for (auto e : arr){e * 2;}//打印数组中的所有元素for (const auto e : arr){cout e ; // 2 4 5 8 10 12 14 16 18 20}cout endl;return 0;
}注意 与普通循环类似可用continue来结束本次循环也可以用break来跳出整个循环。 范围for的使用条件 1. for循环迭代的范围必须是确定的
对于数组而言就是数组中第一个元素和最后一个元素的范围对于类而言应该提供begin和end的方法begin和end就是for循环迭代的范围。
2. 迭代的对象要支持和操作
范围for本质上是由迭代器支持的在代码编译的时候编译器会自动将范围for替换为迭代器的形式。而由于在使用迭代器遍历时需要对对象进行和操作因此使用范围for的对象也需要支持和操作。
四、array容器和forward_list容器
1、array容器
array就是一个静态数组其有两个模板参数第一个模板参数代表的是存储的数据类型第二个是非类型模板参数代表的是存储元素的个数
int main()
{arrayint, 10 a1;arraydouble, 15 a2;return 0;
}array和普通数组最大的区别在于对于越界访问的检查
int main()
{int a[10];cout a[10] endl;//越界不一定能检查出来arrayint, 10 b;cout b[10] endl;//只要越界一定能检查出来return 0;
}总结
array容器的对象是建立在栈区的不适合定义大数组array容器的设计可能是为了代替静态数组因为array容器更安全能够检查除越界的错误而静态数组并不一定能够检查出来。
2、forward_list容器
forward_list容器本质就是一个单链表相比list的区别在于forward_list节省了空间实际使用上使用forward_list的比率还是比较低的还是使用list来的方便。
容器中的一些新方法
如果我们再细细去看会发现基本每个容器中都增加了一些C11的方法但是其实很多都是用得比较少的。
比如提供了cbegin和cend方法返回const迭代器等等但是实际意义不大因为begin和end也是可以返回const迭代器的这些都是属于锦上添花的操作。