大二的时候看过《C++Primer》,了解过C++11,因此就在简历上写上了解C++11,结果就是频频被问到有关C++11。。。发现自己答的并不算太好~
因此,简单总结一下,我在找实习的过程被问到的C++11特性。
1. nullptr
注意在C++中NULL仅仅是define NULL 0的一个宏定义,因此,有时候会产生歧义
比如f(char*)和f(int),参数传NULL的话到底该调用哪个?事实上,在VS下测试这样的函数重载会优先调用f(int),但是f(char *)也是正确的,因此C++引入nullptr来避免这个问题。
更多请参考nullptr, NULL
2. auto, decltype
两者支持类型推导,获取
int a = 0;
decltype(a) b = 3;
cout << b << endl;
3.范围for
常常和auto结合
比如
int arr[] = {1,2,3,4,5};
for(int& e : arr)
{
e = e*e;
}
4.智能指针
基于RAII原则,引入shared_ptr, unique _ptr等等,具体可参见智能指针和RAII
5. 新增容器
array最早是在boost中出现:http://www.boost.org/doc/libs/1_61_0/doc/html/array.html
当时的初衷是希望提供一个在栈上分配的,定长数组,而且可以使用stl中的模板算法。unordered_map unordered_set
同样是来至boost的组件:http://www.boost.org/doc/libs/1_61_0/doc/html/unordered.html
在早期的标准库stl中是只有红黑树map,而没有hash map的。
所以boost提供了unordered这个组件,并且在c++11中进入了标准库。
unordered_ map提供了和map类似的接口,只是map是有序,而unordered_map因为采用hash map的数据结构,所以是无序的。关于unordered_map和map的使用场景:
- 查找速度:unordered_map底层是一个hash _ table,一般来说hash函数,hash冲突解决的好,可以达到O(1),map底层是一棵红黑树,效率大概是O(logn)
- 内存问题,unordered_map是利用了空间换时间,哈希表的元素越少,hash冲突可能性也就越少,效-也就越接近于O(1),但是这样一来,它的空间利用率也越小;所以一般来说,哈希表的装填因子大概在0.7-0.8。
6.右值引用,移动构造函数
分清楚什么是左值和右值,一个比较简易的方法就是能否取地址,能取地址的则为左值。
为什么引入右值机制?主要是为了效率,在
右值一般为临时变量,字面常量也属于右值,因为临时变量往往在后面不可使用或者说不再使用,因此,我们可以steal窃取他们的资源。
我们看一个例子:
class A
{
public:
A()
{
cout << "A()" << endl;
}
A(const A&)
{
cout << "A(const A&)" << endl;
}
};
vector<A> Get()
{
vector<A> tmp;
tmp.push_back(A());
cout << "Get end" << endl << endl;
return tmp;
}
int main()
{
vector<A> v;
v = Get();
return 0;
}
在C99标准下编译后结果如下:
在C++11标准下编译后结果如下:
为什么会这样?
这是因为:
对于Get函数,它会返回一个临时vector < A> 对象,当main函数中,执行到v = Get()时候,它会将v
中现有成员都删除,然后将这个临时对象里面所有内容都拷贝到v中,最后析构这个临时对象。我们知道拷贝复制的代价还是很大,对于这种情况,我们完全可以直接将v中资源和这个临时对象中的资源交换,然后再删除临时对象,这样就避免复制操作,提高了效率。
右值引用必须绑定在右值上,但是我们可以通过std::move函数“绑定左值”
右值引用因为绑定对象即将被销毁,意味着没有人会继续访问他们,所以就可以把他们(的资源)steal过来。
虽然不能将右值引用绑在左值上,但通过利用utility头文件新增的函数模板move,它返回传入对象的右值引用,可以达到 steal的效果。
int &&rr3 = std::move(rr2); // ok
再提醒:一旦使用了move,编译器就默认传入对象已经不打算使用了,是可以被销毁的,move之后该对象的值已经不确定,不要再访问。还有由于对象偷取与复制的差别巨大,不注意会产生非常难定位的bug,所以所有使用move的地方一定要使用全称std::move,给大家以提醒。(其实c++11在algorithm头文件也新增了一个move,参数与意义都与此截然不同)。
移动构造函数等
- move构造函数
对象可以被复制构造,满足条件场景下,自然也就可以被“剪切”构造。move构造函数就用于此,它也属于copy-control系列函数,后者的5个成员现在更新为:拷贝构造函数,复制性质的赋值运算符,move构造函数,move性质的赋值运算符,析构函数。
class base
{
public:
int* p;
base():{p = new(…);}
~base(){delete p;}
base(base&& ref):p(ref.p){ref.p = nullptr;}
};
新创建对象的指针p接管了来源对象指针p所持有的资源,并将后者置空。特别注意move构造函数一定要来源对象内部存储资源的变量设置正确的状态,如指针置空等,避免来源对象析构时内存误释放。
move语义不仅仅用于右值,也用于左值。标准库提供了std::move方法,将左值转换成右值。因此,对于swap函数,我们可以这样实现:
template<class T>
void swap(T& a, T& b)
{
T temp(std::move(a));
a = std::move(b);
b = std::move(temp);
}
参考:
http://www.cnblogs.com/TianFang/archive/2013/01/26/2878356.html