C++11標準由C++標準委員會於2011年8月12日公佈,並於2011年9月出版。雖然長期在使用C++,但是對C++11一直處於觀望,處於很難用起來(編譯器支持等問題),沒有很大興趣去學會的狀態。也許風向變到C++11成爲主流,纔會引起大家的重視和追捧。可是這是常人思維,基本上週圍的精英牛人都已經掌握了這些東西,並且已經開始用於工作當中了。 所以這就是我這樣的常人和牛人的巨大差別。所以我也要跟上節奏,稍稍學習一下。
auto
C++11中引入auto的作用是爲了自動類型推導,將顯而易見的類型設定交給編譯器來做,這將簡化我們的編程工作。而且編譯器本來也要計算右邊值的類型是否和左邊匹配,所以這種自動推導並不會對效率產生不良影響。
auto a; //錯誤,完全無法推導類型
auto i = 5;
auto d = 6.2;
auto str = "Hello World";
auto ch = 'x';
auto func = less<int>(); //函數指針
vector<int> iv;
auto ite = iv.begin(); //長長枚舉類型不用寫了
auto p = new foo() // 對自定義類型進行類型推導
更高級的用法:
template <typename Product, typename Creator>
void processProduct(const Creator& creator) {
Product* val = creator.makeObject();
// do somthing with val
}
可以藉助auto簡化模板參數
template <typename Creator>
void processProduct(const Creator& creator) {
auto val = creator.makeObject();
// do somthing with val
}
decltype
新的操作符 decltype 可以從一個表達式中“俘獲”其結果的類型並“返回”
const vector<int> vi;
typedef decltype (vi.begin()) CIT;
CIT another_const_iterator; //CIT等效vector<int>::iterator
lambda
lambda說簡單了就是一個簡單的匿名函數,可以靈活混跡於代碼當中,不用規規矩矩的聲明函數寫實現。在C++中它的使用格式是:
capture->return-type {body} 其中capture是lambda表達式可以取得的外部變量,paramters是傳入參數,->指向返回值類型,body表達式即函數體。
vector<int> iv{5, 4, 3, 2, 1};
int a = 2, b = 1;
for_each(iv.begin(), iv.end(), [b](int &x){cout<<(x + b)<<endl;});
for_each(iv.begin(), iv.end(), [=](int &x){x *= (a + b);});
for_each(iv.begin(), iv.end(), [=](int &x)->int{return x * (a + b);});
[=]中間的等號代表lambda表達式可以用所有的外部變量。
nullptr
nullptr 是一個新的 C++ 關鍵字,它是空指針常量,它是用來替代高風險的 NULL 宏和 0 字面量的。nullptr 是強類型的:
void f(int); //#1
void f(char *);//#2
//C++03
f(0); //調用的是哪個 f? 甚至還有explicit來專門處理這個問題
//C++11
f(nullptr) //毫無疑問,調用的是 #2
deleted 和 defaulted 函數
struct A
{
A()=default; //C++11
virtual ~A()=default; //C++11
};
=default; 指示編譯器生成該函數的默認實現。這有兩個好處:一是讓程序員輕鬆了,少敲鍵盤,二是有更好的性能。
與 defaulted 函數相對的就是 deleted 函數,它代表函數刪除掉了,如果使用就報錯。如下例,用於阻止拷貝:
struct NoCopy
{
NoCopy & operator =( const NoCopy & ) = delete;
NoCopy ( const NoCopy & ) = delete;
};
NoCopy a;
NoCopy b(a); //編譯錯誤,拷貝構造函數是 deleted 函數
優雅的初始化方法
在引入C++11之前,只有數組能使用初始化列表,其他容器想要使用初始化列表,只能用以下方法:
int arr[3] = {1, 2, 3}
vector<int> v(arr, arr + 3);
在C++11中,我們可以使用以下語法來進行替換:
int arr[3]{1, 2, 3};
vector<int> iv{1, 2, 3};
map<int, string>{{1, "a"}, {2, "b"}};
string str{"Hello World"};
委託構造(代理構造)函數
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"<<end;} //#2 delegating
}
記得曾今用過類似的方式,不過直到今天突然迷糊了,不知道當時是用失敗了,還是編譯器套用了C++11特性才編譯通過的。至少說明這是比較有用的新特性。
右值引用
在 C++03 中的引用類型是隻綁定左值的,C++11 引用一個新的引用類型叫右值引用類型,它是綁定到右值的,如臨時對象或字面量。增加右值引用的主要原因是爲了實現 move 語義。與傳統的拷貝不同,move 的意思是目標對象“竊取”原對象的資源,並將源置於“空”狀態。當拷貝一個對象時,其實代價昂貴且無必要,move 操作就可以替代它。如在 string 交換的時候,使用 move 意義就有巨大的性能提升,如原方案是這樣的:
void naiveswap(string &a, string & b)
{
string temp = a;
a=b;
b=temp;
}
這種方案很傻很天真,很慢,因爲需要申請內存,然後拷貝字符,而 move 就只需要交換兩個數據成員,無須申請、釋放內存和拷貝字符數組:
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);
}
要實現支持 move 的類,需要聲明 move 構造函數和 move 賦值操作符,如下:
class Movable
{
Movable (Movable&&); //move constructor
Movable&& operator=(Movable&&); //move assignment operator
};
C++11 的標準庫廣泛使用 move 語義,很多算法和容器都已經使用 move 語義優化過了。
這一段沒看太明白,不過像QT這樣的庫,幾乎把所有對象都實現成了這種“淺拷貝”方式避免簡單交換產生的不必要性能消耗。
C++11 的標準庫
除 TR1 包含的新容器(unordered_set, unordered_map, unordered_multiset, 和unordered_multimap),還有一些新的庫,如正則表達式,tuple,函數對象封裝器等。下面介紹一些 C++11 的標準庫新特性:
線程庫
從程序員的角度來看,C++11 最重要的特性就是併發了。C++11 提供了 thread 類,也提供了 promise 和 future 用以併發環境中的同步,用 async() 函數模板執行併發任務,和 thread_local 存儲聲明爲特定線程獨佔的數據,這裏(http://www.devx.com/SpecialReports/Article/38883)有一個簡單的 C++11 線程庫教程(英文)。
新的智能指針類
C++98 定義的唯一的智能指針類 auto_ptr 已經被棄用,C++11 引入了新的智能針對類 shared_ptr 和 unique_ptr。它們都是標準庫的其它組件兼容,可以安全地把智能指針存入標準容器,也可以安全地用標準算法“倒騰”它們。
新的算法
主要是 all_of()、any_of() 和 none_of(),下面是例子:
#include <algorithm>
//C++11 code
//are all of the elements positive?
all_of(first, first+n, ispositive()); //false
//is there at least one positive element?
any_of(first, first+n, ispositive());//true
// are none of the elements positive?
none_of(first, first+n, ispositive()); //false
還有一個新的 copy_n:
#include <algorithm>
int source[5]={0,12,34,50,80};
int target[5];
//從 source 拷貝 5 個元素到 target
copy_n(source,5,target);
iota() 算法可以用來創建遞增序列,它先把初值賦值給 *first,然後用前置 ++ 操作符增長初值並賦值到給下一個迭代器指向的元素,如下:
#include <numeric>
int a[5]={0};
char c[3]={0};
iota(a, a+5, 10); //changes a to {10,11,12,13,14}
iota(c, c+3, 'a'); //{'a','b','c'}
是的,C++11 仍然缺少一些很有用的庫如 XML API,socket,GUI、反射——以及自動垃圾收集。然而現有特性已經讓 C++ 更安全、高效(是的,效率更高了,可以參見 Google 的 基準測試結果http://www.itproportal.com/2011/06/07/googles-rates-c-most-complex-highest-performing-language/)以及更加易於學習和使用。
如果覺得 C++ 變化太大了,不必驚恐,花點時間來學習就好了。可能在你融會貫通新特性以後,你會同意 Stroustrup 的觀點:C++11 是一門新的語言——一個更好的 C++。
外部資料:
30分鐘瞭解C++11新特性
C++11中值得關注的幾大變化
開始使用C++11的9個理由
C++11各編譯器支持情況對比
百度百科C++11
微軟官方關於現代C++(C++11)的詳細資料