前言:
虽然11年相较于现在,已经过去了9年时间,而C++11的新特性,我们也在不知不觉中使用。但作为C/C++的一名新手程序员,还想看了解一下11这个神奇的版本,相较于经典的98,做了哪些改动!本文是我的一些了解。
C++11
列表初始化
C++11扩大了初始化列表的使用范围,使其可用于所有的内置类型和用户自定义的类型,使用初始化列表时,可有=,也可以没有
eg:
内置类型:
我们习惯于 int x=10; 而在C++11我们可以有了新的玩法 int x={10};或者 int x {10} ;
自定义类型:
其实,上面的这两种特性属于透明的,我们已经习惯了原来的写法,对于新写法不太感冒
标准容器
新支持了容器可以使用列表进行初始化,我们如果知道了事先要存储的内容不必在调用接口一个个插入了,直接一个初始化列表就可以了。在OJ方面用起来,真是爽极了。
vector<int> v={1,2,3};
list<int> l={1,2,3};
map<int,int> m={{1,1},{7,7},{2,2}};
因为这个我们在C中使用的数组的用法类似,尤其是vector;不得不说这个改动很棒!那么这就有些疑问了?vector等容器怎么修改的,以至于支持的列表初始化呢?
看一个直观重要的东西 initializer_list
这是C++11中新出现的类模板,主要有三个接口迭代器begin(),end(),以及获取其中的元素个数size()。
同时它也是C++11中容器支持列表初始化的关键。
倘若我们仔细观察一下容器的构造函数就会发现C++11后容器的构造函数多一个参数中有 initializer_list 的。
容器支持列表初始化简单的我们可以理解为下面的状况:先将数据存储于initializer_list 中,在赋值给我们的容器。倘若我们自己设计的类也需要支持初始化列表,也可以这么设计
tempalte<class T>
class vector{
vector (initializer_list<T> il)
:_capacity(il.size())
,_size(0)
{
_arr=new T[_capacity];
for(auto e:il)
{
_arr[_size]=e;
++_size;
}
}
private:
T* _arr;
size_t _capacity;
size_t _size;
};
变量类型推导和范围for循环
说到auto其实,我就直接联想到了范围for,这两个的组合真是 百试不爽!
就看最普遍的vector遍历
vector<int> arr={...............};
//不使用范围for
for(size_t i=0;i<arr.size();++i)
{
cout<<arr[i]<<" ";
}
cout<<endl;
//范围for
for(const auto& e:arr )
{
cout<<e<<" ";
}
cout<<endl;
可能这体现的不太明显,但是我们的使用auto+范围for,是我们的代码更加简洁。
并且只要支持迭代器就支持范围for,范围for的应用范围对于我们来说也就足够了。
至于变量推导,我们刚才已经了解了有auto;在C++11中,我们可以使用auto来根据变量初始化或表达式类型来推导变量的实际类型(这意味着使用auto之前,我们必须对后面的变量进行初始化 eg: auto x=1;)。对于某些复杂的类型,我们可以用更加简短的auto进行代替,这样虽然简化了我们的代码,但也让我们的代码显得有些晦涩。
在auto的使用前提,就注定auto不能出现在函数的参数列表和模板参数以及返回值的位置!
类型推导呢,其实是属于运行时类型识别RTTI(run time type identification)即程序运行之后才推导出结果的类型
C++98中,有支持RTTI
typeid 查看类型 //但是不能去定义变量
dynamic_cast 只应用于含有虚函数的继承体系中
对于RTTI,它可以让我们简洁代码,但是不可避免的带来了,程序运行效率的下降问题。
RTTI从这个名字中,我们可以稍稍玩味一下,运行时类型识别;程序只有运行,才能推导出类型;只编译的话,就不会推导出
类型的,那么解决这个问题,就需要一个新的主角:decltype
decltype 类型说明符生成指定表达式的类型。在此过程中,编译器分析表达式并得到它的类型,却不实际计算表达式的值。
可以用它定义变量
decltype与函数
final和override
final主要有两个用处
- 修饰类,使类变为最终类,不能被继承
- 修饰虚函数,该虚函数不能被重写
override:子类重写虚函数,检验是否完成重写。如果不符合重写的条件,则报错
新增加容器
array:即我们c语言中常用的数组
forward_list:单链表
unordered_map和unordered_set :就是我们常说的哈希有关的容器;这两个容器效率特别高
unordered_map和map类似都可以完成key-value场景的搜索
unordered_set和set类似都可以完成key场景的搜索
但是新增的这个两个容器更好些,why?
map和set底层是红黑树,增删查的时间复杂度为O(logN),支持双向迭代器
unordered_map和unordered_set 的底层是哈希表,增删查的时间复杂度可达O(1),支持单向迭代器
如果两者使用效果相同,这两个新容器的诱惑力还是超级大的!
对 默认成员函数的控制
在生活中总有一些不可避免的尴尬,可在代码中 依旧如此!
我们看下面的一个场景,如果你没声明,编译器可以暗自为class创建默认构造函数、拷贝构造函数、赋值重载函数、以及析构函数。唯有当这些函数被调用时,他们才会被编译器创建出来。但是呢?自己也不是太懒,只想写一个拷贝构造函数。但是编译器不通过下面的代码,原因就是编译器更懒,没有生成默认构造函数。之前,在这种情况下就很尴尬!
这就好比你和你心爱的女孩说,这周六有个新电影,你有时间吗?他告诉你“有”;忽然之间断了,于是你傻傻的等她,而她呢?因为你没有显示的告诉“我们去看电影吧”,所以她和闺蜜去吃火锅了!你傻傻的等,她开心的吃!这就很恼火。
但是,毕竟现在到11了,通信条件也好了,不会出现断线的情况,之前的尴尬也就不再有了!我直接说出我的目的,你看着办,还好意思扔下我不管自己去happy嘛
上面的场面好点酷,解决方案也很酷:一个default解决!
在C++11中,我们可以在默认成员函数定义或者声明的时候加上=default,就可以显示的指示编译器生成该函数的默认版本,而运来的尴尬也就没了。用default修饰的函数我们称之为显示缺省函数
显示缺省函数
删除默认函数
代码中的尴尬事件看来不止上面一件
倘若我们不想让一个类有拷贝构造函数,这个要怎么办呢?在98的时候,我们只能力度很小的设置一下:将拷贝构造显示的声明出来,为了防止用户在类外面定义,我们还需要将其设置为private,这样就可以帮助我们达成目的!
这好像出现了一幕场景:你和老婆刚结婚,晚上睡觉发现她总打呼噜,你要是直说吧,感觉不太好,而且可能会被“怎么,你嫌弃我了”,“你怎么回事,我这没漂亮,可能会这样嘛”这样,百般难受!所以你选择旁敲测击!稍稍的提醒解决一下这个问题!这个潜藏的问题,终于经过98到11的时候,老夫老妻,你也磨练出来了,你不在容忍,直说。。。。。。。
后面的自己脑补吧!O(∩_∩)O哈哈~,有些讨厌,但不好意思,我们的主角来了! delete
在C++11中,只要该成员默认函数声明上加上=delete;该语法就会只是编译器不生成对应函数的默认版本。这种函数,我们称之为删除函数
我们的第一弹这到这里了,还有一些新特性,会放在接下来的博文中,敬请期待!
各位看官!
故事好不好,全看各位赏!创造不易,动手点个赞哦!
注:如果本篇博客有任何错误和建议,欢迎伙伴们留言,你快说句话啊!