C++11 中的新特性
Table of Contents
自从 1998 年 C++ 标准通过后,历经 13 年,新的标准 C++11 才完善。 C++ 发明人 Bjarne Stroustrup 说“C++11 感觉象一门新语言”。实际上,变化的确很大。不过,最初实现垃圾回收的 (GC) 的愿望仍然没有在新标准中体现。
核心语言特性变化分述如后,更多的参见 C++11 FAQ中文版。
Lambda 表达式
基本形式:
[capture] (parameters) -> return-type {body}
假如想计算字符串的大写字母个数,可用如下代码:
int main() { char s[]="Hello World!"; int Uppercase = 0; //modified by the lambda for_each(s, s+sizeof(s), [&Uppercase] (char c) { if (isupper(c)) Uppercase++; }); cout<< Uppercase<<" uppercase letters in: "<< s<<endl; }
自动类型推导和 decltype
以 C++11 中可以不用指定对象类型即可声明一个对象,例如:
auto x = 0; // X 为 int 型,因为 0 属于 int 类型 auto c = 'a'; // char auto d = 0.5; // double auto national_debt = 14400000000000LL; // long long
当对象类型很冗长,或者是自动生成的 (如在模版中) 对象,比如:
void func(const vector<int> &vi) { vector<int>::const_iterator ci=vi.begin(); }
可以将 iterator 声明改为:
auto ci = vi.begin();
C++11 中提供了一种方法来返回对象或表达式的类型,即新的运算符 decltype
:
const vector<int> vi; typedef decltype (vi.begin()) CIT; CIT another_const_iterator;
统一的初始化语法
C++ 至少有四种初始化表达方式,有些是相互重叠的。
参数的初始化象这样:
std::string s("hello"); int m=int(); //default initialization
也可以使用 =
达到同样的目的:
std::string s="hello"; int x=5;
对于 POD aggregates, 可使用花括号:
int arr[4]={0,1,2,3}; struct tm today={0};
最终,结构体使用成员初始化方法:
struct S { int x; S(): x(0) {} };
方法丰富也容易混淆,这不仅仅是对初学者。在 C++11 中,使用统一的花括号标识:
class C { int a; int b; public: C(int i, int j); }; C c {0,0}; //C++11 only. Equivalent to: C c(0,0); int* a = new int[3] { 1, 2, 0 }; // C++11 only class X { int a[4]; public: X() : a{1,2,3,4} {} //C++11, member array initializer };
对于容器类,可以对 push_back()
调用说再见了。在 C++11 中可以这样初始化容器类:
// C++11 container initializer vector<string> vs = { "first", "second", "third" }; map singers = {{ "Lady Gaga", "+1 (212) 555-7890" }, { "Beyonce Knowles", "+1 (212) 555-0987" }};
同时, C++11 还支持在类定义中初始化数据成员:
class C { int a=7; // C++11 only public: C(); };
delete
和 default
函数
可以这种形式来声明函数:
struct A { A() = default; // C++11 virtual ~A()=default; // C++11 };
这样的函数称为 默认函数, 其中 = default
部分告诉编译器要生成默认实现的函数。
与默认函数相反的是 禁用函数:
int func() = delete;
禁用函数可用于阻止对象拷贝等。比如:
struct NoCopy { NoCopy & operator =( const NoCopy & ) = delete; NoCopy ( const NoCopy & ) = delete; }; NoCopy a; NoCopy b(a); //compilation error, copy ctor is deleted
空指针 nullptr
空指针常量 nullptr
替换了问题多多的 NULL
宏。空指针 nullptr
是强类型的:
void f(int); // #1 void f(char *);// #2 // C++03 f(0); // which f is called? // C++11 f(nullptr) // unambiguous, calls #2
nullptr
适用于所有的指针类别,包括函数指针和数据指针:
const char *pc = str.c_str(); // data pointers if (pc!=nullptr) cout << pc << endl; int (A::*pmf)() = nullptr; // pointer to member function void (*pmf)() = nullptr; // pointer to function
构造函数委派
C++11 中的构造函数可以调用同一个类的另一个构造函数:
class M { // C++11 delegating constructors int x, y; char *p; public: M(int v) : x(v), y(0), p(new char [MAX]) {} //#1 target M(): M(0) {cout<<"delegating ctor"<<endl;} //#2 delegating };
右值引用
旧版本中赋值语句的右边永不会改变,这样做数值交换时往往效率很低,比如:
void naiveswap(string &a, string &b) { string temp = a; a=b; b=temp; }
在交换数据时如果只是拷贝指针,而不需要再进行内存分配,这会很高效。比如:
void moveswapstr(string& empty, string & filled) { //pseudo code, but you get the idea size_t sz = empty.size(); const char *p = empty.data(); //move filled's resources to empty empty.setsize(filled.size()); empty.setdata(filled.data()); //filled becomes empty filled.setsize(sz); filled.setdata(p); }
C++11 中引入“右值引用” (R-value reference), 标识为:
typename &&
它实现了 move semantics 操作,与上述的拷贝指针操作类似,不再需要重新分配内存。 C++11 STL 中广泛使用了右值引用,很多算法和容器的性能都被优化。
STL 标准类库
线程库
新的智能指针类
在 C++11 中,以前的 auto_ptr
用法被废弃,新引入了两个指针类:
shared_ptr
– 单纯的引用计数指针。unique_ptr
– 取代auto_ptr
.
新的算法
定义了一些新的算法: all_of()
, any_of()
, none_of()
. 例如,将判断函数
ispositive()
应用到列表的范围 [first,first+n]
中:
#include <algorithm> // C++11 code // 全部元素都是正的? all_of(first, first+n, ispositive()); //false // 至少有一个为正的? any_of(first, first+n, ispositive());//true // 一个正的都没有吗? none_of(first, first+n, ispositive()); //false
新的 copy_n
算法。比如用 copy_n()
来拷贝数组中的 5 个元素到另一个中:
#include int source[5]={0,12,34,50,80}; int target[5]; //copy 5 elements from source to target copy_n(source,5,target);
新算法 iota()
可用于创建值递增的序列:给 *first
赋个初值,后序的每个元素逐个加一。比如:
include <numeric> int a[5] = {0}; char c[3] = {0}; iota(a, a+5, 10); // 将 a 变为 {10,11,12,13,14} iota(c, c+3, 'a'); // 将 c 变为 {'a','b','c'}