襄阳seo费用,seo竞价培训,做网站网页需要多久,做海外房产最好的网站距离C11版本发布已经过去那么多年了#xff0c;为什么还称为新特性呢#xff1f;因为笔者前面探讨的内容#xff0c;除了auto#xff0c;范围for这些常用的#xff0c;基本上是用着C98的内容#xff0c;虽说C11已经发布很多年#xff0c;却是目前被使用最广泛的版本。因… 距离C11版本发布已经过去那么多年了为什么还称为新特性呢因为笔者前面探讨的内容除了auto范围for这些常用的基本上是用着C98的内容虽说C11已经发布很多年却是目前被使用最广泛的版本。因为新版本的发布编译器要很长时间才能支持新语法新特性有一句话说得挺好C/C之所以强大是因为它们的编译器很强大。C23版本已经发布但真想普遍使用不知道还得多少年呢尤其是C/C这种基本用于底层架构的语言底层代码不敢改也不愿意改。但不管怎么说日后的开发是离不开C11的C11可以说是C历年来最大的版本更新多了很多非常强大的东西但也多了很多鸡肋内容太多太多不可能全部提完。故而笔者着重讲述必须要掌握的用处一般的就简单提提剩下的大家碰到了再查C11新特性部分将分为多篇内容让我们开始吧 目录
array forward_list
autotypeiddecltype
范围for
统一的列表初始化
统一的列表初始化原理
nullptr array forward_list C11新更新了两个容器分别是arrayforward_list array本质上是对C语言的数组进行了一层封装加上了模板迭代器等功能 array的定义如上它也是一个静态数组声明时需要指定类型和大小整体使用下来并不比C语言的数组强多少唯一比C语言数组好用的就是会检查下标我们知道C语言的数组越界读是不会报错的越界写也仅是抽查所以不小心写错下标可能会造成一些问题 但是vector也能检查下标呀我为何不使用vector呢况且vector功能比array强大多了所以array算是C11里比较鸡肋的一个 forward_list是新推出的单链表在C推出STL库时只有listlist的底层实现我们前面也探讨过了本质是双链表。forward_list算是list的青春版因为双链表的节点要链接前后所以会比单链表多存储一个指针大小如果在内存空间特别紧凑并且单链表足够满足使用场景可以考虑使用forward_list其基础功能如下只能头插和尾插 autotypeiddecltype autotypeiddecltype都是C11推出的和类型相关的特性功能 auto是语法糖为什么叫语法糖因为让人用的舒服auto是很好用的在一些场景下有的类型被层层封装导致其类型名很长有了auto就不用我们自己去推断类型 这仅是auto应用场景之一后面我们会见到大量使用auto的场景例如范围for就是使用auto来推导要迭代的类型auto是根据初始值来进行类型推导的也就是说你要使用auto推导必须给出一个初始值否则没有意义 auto test;
//没有初始值无意义的推导 如果你想把某个变量或者表达式的类型给打印出来看看那么就可以使用typeid具体用法如下图typeid是一个类把想知道类型的变量或者表达式传过去调用name 调用name之后会返回一个字符串字符串的内容即是推导出的类型 如果现在提一个过分一点的要求我不仅要你推导出类型还要用你推导的结果再声明出同类型的对象使用typeid是做不到了因为它返回的是字符串不可能作为类型声明符但是decltype可以做到看看decltype是如何使用的 上图用decltype推导出test_1的类型并用推导结果声明了变量test_2 这里的应用场景看着比较傻类型推导在模板里应用比较广泛如下 按照常规方法是不好解决的因为我们不明确 T1 和 T2的类型到底是什么就没有办法给变量ret_val确定类型但是现在我们有了auto 和 decltype就很容易解决这个问题 范围for 范围for也是C11中使用体验不错的语法糖范围for本质就是循环遍历对象范围for的出现帮我们节省了不少时间如下列程序 不仅内置类型可以用容器也是可以使用范围for的如下图程序 只要容器支持迭代器那么它就可以使用范围for因为容器使用范围for本质还是在调用容器中实现的迭代器 但是范围for使用起来方便不少使用汇编可以一窥细节 //测试代码
int main()
{vectorint test;for (int i 1; i 10; i){test.push_back(i);}//迭代器auto it test.begin();while (it ! test.end()){cout *it ;it;}cout endl;//范围forfor (auto t : test){cout t ;}return 0;
} 可以看出两者都是在调用迭代器我们手动调用的begin()和end()是经过封装过的范围for则直接调用迭代器的底层实现在容器中范围for本质和迭代器没区别但是用的更省心 使用范围for时如果auto推导类型后跟上就是引用调用可以读写原数据 如果没有跟上那么就是传值调用修改并不会影响原数据 如果只想读不想写的话推荐 const auto 这种写法减少拷贝消耗 统一的列表初始化 平时我们给C语言的数组进行初始化操作时可以使用一对花括号进行赋值 { } //使用花括号给数组进行初始化赋值
int arr[] {1, 2, 3, 4, 5, 6}; 其实这样的初始化还挺好用的我们平时使用vector进行赋值时就没那么方便如果赋值有顺序还好没顺序的话还要自己手动push_back于是C11提出了统一初始化列表也就是说让STL中的容器也够支持使用 { } 来进行赋值如下图 可不仅仅是只有vector能使用其它容器也是支持的如下图的list和map 你甚至可以直接不写赋值符号 同样能完成初始化不仅可以用于数组容器对于单个内置类型也是可以使用{}来初始化的如下图 除了内置类类型和STL库中的容器支持这种初始化自定义类型也是支持的 需要注意的是使用{}初始化自定义类型是去调用自定义类型的构造函数由此看来C11之后确实可以统一使用{ }来初始化这也是为什么叫统一的列表初始化 统一的列表初始化原理 像数组可以使用{}进行初始化可以理解毕竟原生的编译器就支持但是容器也支持{}初始化是怎么做到的呢 其实实现原理也不难我们以vector为例既然是初始化那我们就紧盯构造函数打开资料库查查C11的构造函数有没有发生变化 果然我们发现构造函数中多了一个initializer list而这就是统一列表初始化实现的秘密可以看出该构造函数使用的是initializer_list我们查一下这是个什么东西 我们大概能理解使用{ }时会将里面的内容放到initializer_list容器中存放起来然后把该容器内的值拷贝给vector如此就完成了初始化操作知道原理了我们可以自己尝试给之前写的vector也添加这么个功能 vector(std::initializer_listT _lt)
{_begin new T[_lt.size()];_finish _begin _lt.size();_end _begin _lt.size();auto _vtp _begin;auto _ltp _lt.begin();while (_ltp ! _lt.end()){*_vtp *_ltp;_vtp;_ltp;}
} 这个只能构造若想拷贝赋值的话可以重载一个operator( std::initializer_listT _lt) 具体定义如下别忘了包含头文件initializer_list vectorT operator(std::initializer_listT _lt)
{vectorT tmp(_lt);std::swap(_start, tmp._start);std::swap(_finish, tmp._finish);std::swap(_endofstorage, tmp._endofstorage);return *this;
} nullptr 出现nullptr是因为C错误的将库中的NULL定义为0这就会导致很多问题因为NULL不仅表示一个空指针还是一个字面常量值0如下述代码 简单写个代码验证其中的危害性 int main()
{int p NULL;cout p endl;int t 0;if (t NULL) cout t是一个空指针 endl;return 0;
} 运行结果如上t被错误的判断为一个空指针事实上t连指针都不是。错已经错了直接改NULL会影响原先的代码为了解决这个问题C又推出了nullptr来代替NULL nullptr则是正确的定义 (void*)0所以C中请使用nullptr来表示空指针 至此本篇文章就结束了总体下来还算轻松很多内容都是见过多次的老朋友了其中也多了不少好用的特性像统一初始化列表范围for这种就快快用起来吧