长沙网页设计公司哪家好,湖南seo推广,房产政策最新消息,伊春网站推广文章目录 一、C11简介二、统一的列表初始化2.1 { } 初始化2.2 列表初始化在内置类型上的应用2.3 列表初始化在内置类型上的应用2.4 initializer_list2.4.1 {1, 2, 3} 的类型2.4.2 initializer_list 使用场景2.4.3 模拟实现的 vector 中的 { } 初始化和赋值 三、声明3.1 auto3.1… 文章目录 一、C11简介二、统一的列表初始化2.1 { } 初始化2.2 列表初始化在内置类型上的应用2.3 列表初始化在内置类型上的应用2.4 initializer_list2.4.1 {1, 2, 3} 的类型2.4.2 initializer_list 使用场景2.4.3 模拟实现的 vector 中的 { } 初始化和赋值 三、声明3.1 auto3.1.1 auto使用细则3.1.2 不能使用auto的场景 3.2 decltype3.3 nullptr 四、STL中的一些变化五、结语 一、C11简介
在 2003 年 C 标准委员会曾经提交了一份技术勘误表简称 TC1使得 C03 这个名字已经取代了 C98成为 C11 之前的最新 C 标准名称不过由于 C03TC1主要是对 C98 标准中的漏洞进行修复语言的核心部分则没有改动因此人们习惯性的把两个标准合并称为 C98/03 标准。从 C0x 到 C11C 标准十年磨一剑第二个真正意义上的标准姗姗来迟。相比于 C98/03C11 则带来了数量客观的变化其中包含了约 140 个新特性以及对 C03 标准中约 600 个缺陷的修正这使得 C11 更像是从 C98/03 中孕育出的一种新语言。相比较而言C11 能更好的用于系统开发和库开发、语法更加泛化和简单化、更加安全和稳定不仅功能更强大而且能提升程序员的开发效率公司实际项目开发中也用的比较多所以我们要作为一个重点去学习。C11 增加的语法特性篇幅非常多没办法一一为大家讲解所以我会挑一些实际中比较实用的语法分享给大家。
小故事 1998 年是 C 标准委员会成立的第一年本来计划以后每五年更新一次标准C 国际标准委员会在研究 C03 的下一个版本的时候一开始计划是 2007 年发布所以最初这个标准叫 C07。但是到 2006 年的时候官方觉得 2007 年肯定完不成 C07而且官方觉得 2008 年可能也完不成。最后干脆叫 C0x。x 的意思是不知道到底能在 07 还是 08 还是 09 年完成。结果 2010 年的时候也没完成最后在 2011 年终于完成了新的 C 标准所以最终定名为 C11。
二、统一的列表初始化
2.1 { } 初始化
在 C98 中标准允许使用花括号 { } 对数组或者结构体元素进行统一的列表初始化。如下
struct Point
{int _x;int _y;
};int main()
{int arrya1[] { 1, 2, 3, 4 };//列表初始化初始化数组int array2[5] { 0 };//列表初始化初始化数组Point p { 1, 2 };//列表初始化初始化结构体元素Point array3[] { {1, 2}, {3, 4}, {5, 6} };//列表初始化初始化结构体数组return 0;
}2.2 列表初始化在内置类型上的应用
C11 扩大了用大括号括起来的列表初始化列表的使用范围使其可用于所有的内置类型和用户自定义的类型使用初始化列表时可以添加等号也可以不添加。建议在使用的过程中别去掉等号
struct Point
{int _x;int _y;
};int main()
{int arrya1[] { 1, 2, 3, 4 };int array2[5] { 0 };Point p { 1, 2 };Point array3[] { {1, 2}, {3, 4}, {5, 6} };//C11 中列表初始化应用在内置类型上int x1 10;int x2 { 20 };int x3{ 30 };//不带等号//C11 中列表初始化也可以适用于 new 表达式中int* p1 new int[5]{100};return 0;
}2.3 列表初始化在内置类型上的应用
创建对象时也可以使用列表初始化的方式来调用构造函数初始化。
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(2022, 1, 1); // old style// C11支持的列表初始化这里会调用构造函数初始化Date d2{ 2022, 1, 2 };Date d3 { 2022, 1, 3 };//这里本质上是 C11 实现了多参数的构造函数支持隐式类型转换return 0;
}小Tips如果在 Date 类的构造函数前面加上 explicit 关键词进行修饰那么 Date d3 { 2022, 1, 3 }; 就会出现编译报错因为这条语句本质上是因为 C11 中实现了多参数的构造函数支持隐式类型转换。Date d2{ 2022, 1, 2 }; 没事任然可以正常运行。
void Test()
{//Date d1 { 2003, 10, 18 };//(错误)隐式类型转换的过程中会产生临时的中间变量这个中间变量具有常性const Date d1 { 2003, 10, 18 };//(正确)
}2.4 initializer_list
2.4.1 {1, 2, 3} 的类型
int main()
{auto il { 1, 2, 3 };cout typeid(il).name() endl;return 0;
}2.4.2 initializer_list 使用场景
initializer_list 一般是作为构造函数的参数C11 对 STL 中的不少容器就增加了 initializer_list 作为参数的构造函数这样初始化容器对象就更方便了。也可以作为 operator 的参数这样就可以用大括号赋值。
int main()
{vectorint v { 1,2,3,4 };//调用vector中形参为 initializer_list 的构造函数listint lt { 1,2 };//调用list中形参为 initializer_list 的构造函数// 这里{sort, 排序}会先初始化构造一个pair对象mapstring, string dict { {sort, 排序}, {insert, 插入} };// 使用大括号对容器赋值v { 10, 20, 30 };//调用vector中形参为 initializer_list 的赋值运算符重载函数Date d1 { 2003, 4, 5 };//这里是直接调用两个参数的构造函数 --- 隐式类型转换return 0;
}小Tips需要注意上面代码中创建 Date 类型的对象 d1本质上是隐式类型的转换。vector、list、map 都是容器它们存储的数据个数可多可少并不确定所以它们都提供了形参为 initializer_list 的构造函数这样就方便我们将任意数量的元素存到容器中。而 Date 作为一个日期类对象它的三个成员变量是固定的所以不需要提供形参为 initializer_list 的构造函数。因此上面创建 d1 本质上是隐式类型转换。同理dict 是一个 map 类型的容器它里面存的每个元素都是一个 pair因此先要构建一个 pair 类型的对象{sort, 排序} 本质上也是通过多参数构造函数的隐式类型转换去构造一个 pair 对象。总结在创建 dict 对象的时候先通过隐式类型转换去构建一个 pair 类型的对象再通过列表初始化去穿件 map 类型的对象 dict。
2.4.3 模拟实现的 vector 中的 { } 初始化和赋值
//用列表初始化的构造函数
vector(initializer_listT lt)
{reserve(lt.size());//一次性开好避免push_back中多次调用提高斜率for (auto e : lt){push_back(e);}
}//用列表进行赋值
vectorT operator(initializer_listT il)
{vectorT tmp(il);swap(tmp);return *this;
}三、声明
C11 提供了多种简化声明的方式尤其是在使用模板时。
3.1 auto
在 C98 中 auto 是一个存储类型的说明符表明变量是局部自动存储类型但是局部域中定义局部的变量默认就是自动存储类型所以 auto 就没什么价值了。C11 中废弃 auto 原来的用法将其用于实现自动类型推断。这样要求必须进行显示初始化让编译器将定义对象的类型设置为初始化值的类型。
int main()
{int i 10;auto p i;auto pf strcpy;cout typeid(p).name() endl;cout typeid(pf).name() endl;mapstring, string dict { {sort, 排序}, {insert, 插入} };//mapstring, string::iterator it dict.begin();auto it dict.begin();return 0;
}3.1.1 auto使用细则
auto与指针和引用结合起来使用用auto声明指针类型时用auto和aauto*没有任何区别但是auto声明引用类型时必须要加如下如果c不加的话就是x的一份拷贝。
int main()
{int x 10;auto a x;//根据右边推出a是一个指针类型auto* b x;//右边必须是一个地址因为前面加了*auto c x;//引用必须要加
}在同一行定义多个变量当在同一行声明多个变量的时候这些变量必须是相同的类型否则编译器会报错因为编译器实际只对第一个类型进行推导然后用推导出来的类型定义其他变量。
int main()
{auto a 10, b 30;auto c 60, d 1.1;//该行编译失败c和d的初始化类型不同
}3.1.2 不能使用auto的场景
auto不能作为函数的参数
//错误编译器无法对x的实际类型进行推导
void Text(auto x)
{}·auto不能直接用来声明数组
void Text()
{//auto arr[] { 1, 2, 3 };//错误写法请勿模仿int arr[] {1, 2, 3}//这才是正确写法
}小Tipsauto在实际中常被用在基于范围的for循环中、还有lambda表达式中、其次就是一些非常非常长的类型也会用auto进行替换。
3.2 decltype
关键字 decltype 将变量的类型声明为表达式指定的类型。
// decltype的一些使用使用场景
templateclass T1, class T2
void F(T1 t1, T2 t2)
{decltype(t1 * t2) ret;cout typeid(ret).name() endl;
}
int main()
{const int x 1;double y 2.2;decltype(x * y) ret; // ret的类型是doubledecltype(x) p; // p的类型是int*cout typeid(ret).name() endl;cout typeid(p).name() endl;F(1, a);mapdecltype(x), decltype(x) mp;//做模板实参return 0;
}小Tipsdecltype(x) 与 typeid(x).name() 的区别在于前者获取到 x 的类型后可以在后面紧接着去定义一个和 x 类型相同的变量或者将该类型作为模板的实参。而后者只能获取到 x 的类型将其以字符串的形式打印出来不能在其后面接着定义变量。
3.3 nullptr
由于 C 中 NULL 被定义成字面量0这样就可能会带来一些问题因为0既能表示指针常量又能表示整型常量。所以出于清晰和安全的角度考虑C11 中新增了 nullptr用于表示空指针。
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif四、STL中的一些变化
C11 新增了一些容器。用橘色圈起来的就是 C11 中的一些几个新容器但是实际最有用的是 unordered_set 和 unordered_map。这个在【C杂货铺】一文带你走进哈希 中给大家介绍过了其他的容器大家了解一下即可。 array是一个静态数组。即它可以存储多少数据是在定义该 arrary 对象的时候就确定好的它和我们自己定义的数组区别在于他对越界的检查十分严格。
int main()
{int ar1[10];arrayint, 10 ar2;ar1[15] 1;return 0;
}可以看出对于我们自己定义的数组越界访问程序可能不会报错任然可以正常退出。因为 ar1[15] 1 本质上是对指针的解引用即 *(ar1 15) 1。
int main()
{int ar1[10];arrayint, 10 ar2;ar2[15] 1;return 0;
}对 array 对象越界访问最终程序崩溃了。因为这里 ar2[15] 1 本质上是去调用 operator[ ] 这个函数该函数内部进行了检查所以一旦越界访问程序就会崩溃。总之array 这个容器提供出来多少显得有一点鸡肋更多情况下大家还是更喜欢用 vector。
forward_list是一个单链表只支持单向迭代器并且只支持头插和头删。尾删因为要找前一个结点效率会比较低它的 insert 也是在当前结点的后面进行插入。
容器中的一些新方法再仔细的去看可以发现基本每个容器中都增加了一些 C11 的方法但是其实还有很多都是用的比较少的。 比如提供了 cbegin 和 cend 方法返回 const 迭代器等等但是实际意义并不大因为 begin 和 end 也是可以返回 const 迭代器的这些都属于锦上添花的操作。 其次所有的容器都新增了{}列表初始化的构造函数这一点用途还是蛮大的。 所有的容器都提供了 emplace 系列的接口这个接口可以提高插入的性能。这里还涉及两个其他的知识点右值引用和模板的可变参数将在后面的文章中为大家讲解。其次C11 中对 push_back 接口进行了升级新增了形参为右值引用的版本这也使得插入的性能得以提升。 并且 C11 中还新增了移动构造和移动赋值这让深拷贝的性能提升了 90%。
五、结语
今天的分享到这里就结束啦如果觉得文章还不错的话可以三连支持一下春人的主页还有很多有趣的文章欢迎小伙伴们前去点评您的支持就是春人前进的动力