本文章轉載自http://blog.csdn.net/hzyong_c/article/details/8273884,尊重版權,自己感覺不錯,主要用於收藏
最近學習了C++11的新特性,將學習內容整理下來以鞏固記憶,C++11的新特性,可以分爲兩部分,第一部分是C++11核心語言的特性,第二部分是STL標準庫的新特性。學習C++11主要參考了wiki上的一篇文章,在介紹右值引用的時候還參考了MSDN上一篇文章,由於這兩篇文章寫的時間比較早,和實際有些出入,我的開發環境是win8,vs2012,很多C++11特性還沒支持,所以只整理了vs2012已經支持了的特性。
第一部分:核心語言的特性
一. 右值引用,move語義,完美轉發
1. 左值(lvalue)和右值(rvalue)的概念
c++11引入一種新式引用,名曰右值引用,語法:Type&& , const Type&&,區別於之前的&標示的左值引用。爲理解右值引用,先要理解左值和右值的概念。
左值,在表達式中,表達式結束時候不會消失,如:obj , *ptr , ptr[index] , ++x
右值,在表達式中,是臨時的,表達式結束就會“蒸發”,如:1729 , x + y , std::string("meow") , x++
區分左值和右值,還有另一種方法:能否取得其地址。
如果能取得其地址,是左值,如:&obj , &*ptr , &ptr[index] , &++x是合法的,是左值;
如果不能取得其地址,是右值,如:&1729 , &(x + y) , &std::string("meow") , &x++ 都是不合法的,是右值。
不管是左值還是右值,它要麼是modifiable的,要麼是const的,如下:
string one("cute");
const string two("fluffy");
string three() { return "kittens"; }
const string four() { return "are an essential part of a healthy diet"; }
one; // modifiable lvalue
two; // const lvalue
three(); // modifiable rvalue
four(); // const rvalue
2. 左值引用和右值引用的綁定特性
左值引用和右值引用各包含modifiable value和const value,故可以分爲4種引用形式:
modifiable lvalue reference
const lvalue reference
modifiable rvalue reference
const rvalue reference
下面這個示例是測試4種引用的綁定特性,每種引用都試圖綁定這4種引用的值。
<span style="font-size:14px;"> #include <iostream>
using namespace std;
// 測試左值引用和右值引用的綁定特性
/**
* 左值引用和右值引用各包含modifiable value和const value,故可以分爲4種引用形式:
* 1. modifiable lvalue reference
* 2. const lvalue reference
* 3. modifiable rvalue reference
* 4. const rvalue reference
* 下面這個示例是測試4種引用的綁定特性,每種引用都試圖綁定這4種引用的值。
*/
string modifiable_rvalue() {
return "cute";
}
const string const_rvalue() {
return "fluffy";
}
int main() {
string modifiable_lvalue("kittens");
const string const_lvalue("hungry hungry zombies");
// A: testing modifiable lvalue reference
string& a = modifiable_lvalue;
string& b = const_lvalue; // cannot convert from 'const std::string' to 'std::string &'
string& c = modifiable_rvalue();
string& d = const_rvalue(); // cannot convert from 'const std::string' to 'std::string &'
// B: testing const lvalue reference
const string& e = modifiable_lvalue;
const string& f = const_lvalue;
const string& g = modifiable_rvalue();
const string& h = const_rvalue();
// C: testing modifiable rvalue reference
string&& i = modifiable_lvalue; // cannot convert from 'std::string' to 'std::string &&'
string&& j = const_lvalue; // cannot convert from 'const std::string' to 'std::string &&'
string&& k = modifiable_rvalue();
string&& l = const_rvalue(); // cannot convert from 'const std::string' to 'std::string &&'
// D: testing const rvalue reference
const string&& m = modifiable_lvalue; // cannot convert from 'std::string' to 'const std::string &&'
const string&& n = const_lvalue; // cannot convert from 'const std::string' to 'const std::string &&'
const string&& o = modifiable_rvalue();
const string&& p = const_rvalue();
return 0;
} </span>
通過上面例子得出的結論:1) 左值引用和右值引用: modifiable references不能綁定const修飾的值.
2.)右值引用不能綁定左值引用,無論是否const修飾的值.
利用重載函數檢查自動綁定:
<span style="font-size:14px;"> #include <iostream>
#include <string>
using namespace std;
// 利用重載函數檢查自動綁定
void meow(string& s) {
cout << "meow(string&): " << s << endl;
}
void meow(const string& s) {
cout << "meow(const string&): " << s << endl;
}
void meow(string&& s) {
cout << "meow(string&&): " << s << endl;
}
void meow(const string&& s) {
cout << "meow(const string&&): " << s << endl;
}
string rvalue_func() {
return "rvalue_func()";
}
const string const_rvalue_func() {
return "const_rvalue_func()";
}
int main() {
string lvalue("lvalue");
const string const_lvalue("const_lvalue");
meow(lvalue);
meow(const_lvalue);
meow(rvalue_func());
meow(const_rvalue_func());
return 0;
} </span>
運行結果:
meow(string&): lvalue
meow(const string&): const_lvalue
meow(string&&): rvalue_func()
meow(const string&&): const_rvalue_func()
請按任意鍵繼續. . .
有個值得注意的地方:當有 const Type& 和 Type&& 重載時,modifiable rvalues bind to Type&&,其他的都bind to const Type&.
3. Move語義
std::move是獲得右值的方式,通過move可以將左值轉爲右值。
<span style="font-size:14px;"> #include <iostream>
#include <utility>
#include <string>
#include <vector>
using namespace std;
int main()
{
std::string str = "Hello";
std::vector<std::string> v;
// uses the push_back(const T&) overload, which means
// we'll incur the cost of copying str
v.push_back(str);
std::cout << "After copy, str is \"" << str << "\"\n";
// uses the rvalue reference push_back(T&&) overload,
// which means no strings will copied; instead, the contents
// of str will be moved into the vector. This is less
// expensive, but also means str might now be empty.
v.push_back(std::move(str));
std::cout << "After move, str is \"" << str << "\"\n";
std::cout << "The contents of the vector are \"" << v[0] << "\", \"" << v[1] << "\"\n";
} </span>
在 C++11,一個std::vector的 "move 構造函數" 對某個vector的右值引用可以單純地從右值複製其內部 C-style 數組的指針到新的 vector,然後留下空的右值。這個操作不需要數組的複製,而且空的暫時對象的解構也不會摧毀存儲器。傳回vector暫時對象的函數只需要傳回std::vector<T>&&。如果vector沒有 move 構造函數,那麼複製構造函數將被調用,以const std::vector<T>
&的正常形式。 如果它確實有 move 構造函數,那麼就會調用 move 構造函數,這能夠免除大幅的存儲器配置。 二. 類型推導
有被明確初始化的變量可以使用 auto 關鍵字
使用auto可以減少冗餘代碼,舉例而言,程序員不用寫像下面這樣:
<span style="font-size:14px;">for (vector<int>::const_iterator itr = myvec.cbegin(); itr != myvec.cend(); ++itr) </span>
而可以用更簡短的:
<span style="font-size:14px;">for (auto itr = myvec.cbegin(); itr != myvec.cend(); ++itr) </span>
這裏的cbegin()和cend也是c++11新加入的,因爲begin(),和end()函數在容器中都有兩個,一個返回iterator,另一個返回const_iterator,因爲有auto關鍵字,編譯器將右值推導成哪個不明確,所以對容器內容只讀時推薦使用cbegin()和cend()
三. 以範圍爲基礎的 for 循環
for 語句將允許簡單的範圍迭代:
<span style="font-size:14px;"> int my_array[5] = {1, 2, 3, 4, 5};
for (int &x : my_array)
{
x *= 2;
} </span>
四. Lambda函數與表示式
在調用C++標準程序庫算法函數諸如算法函數諸如sort和find_if時候,第3個參數往往需要輸入一個函數對象,既麻煩又冗贅,還好C++11有了Lambda的支持,舉例而言,程序員不用寫像下面這樣:
<span style="font-size:14px;"> // 查找字符串長度是5的Data
struct Cmpare
{
Cmpare(int n) : len_(n)
{}
bool operator()(const Data& d)
{
return (d.data_.length() == len_);
}
private:
int len_;
};
vector<Data>::iterator it = std::find_if(my_vec.begin(), my_vec.end(), Cmpare(5)); </span>
而可以用Lambda函數代替:
<span style="font-size:14px;"> int find_len = 5;
it = std::find_if(my_vec.begin(), my_vec.end(), [find_len](const Data& d) {
return (d.data_.length() == find_len);
}); </span>
Lambda函數體內需要什麼變量,就在方括號中指明,方括號內容的含義:[] // 沒有定義任何參數。使用未定義參數會導致錯誤。
[x, &y] // x 以傳值方式傳入,y 以傳引用方式傳入。
[&] // 任何被使用到的外部參數皆以引用方式使用。
[=] // 任何被使用到的外部參數皆以傳值方式使用。
[&, x] // x 顯式地以傳值方式加以使用。其餘參數以傳入方式使用。
[=, &z] // z 顯式地以引用方式加以使用。其餘參數以傳值方式使用。
在成員函數中指涉對象的 this 指針,必須要顯式的傳入 lambda 函數, 否則成員函數中的 lambda 函數無法使用任何該對象的變量或函數。
[this]() { this->SomePrivateMemberFunction(); };
五. 顯示虛函數重載 override final關鍵字
爲防止子類重載的函數與基類函數不一致的情況發生,在編譯期期捕獲倒錯誤,語法如下:
<span style="font-size:14px;"> struct Base {
virtual void some_func(float);
};
struct Derived : Base {
virtual void some_func(int) override; // Error: Derive::some_func 並沒有 override Base::some_func
virtual void some_func(float) override; // OK:顯示重載
}; </span>
C++11也提供關鍵字final,用來避免類被繼承,或是基類的函數被改寫:
struct Base1 final { };
struct Derived1 : Base1 { }; // Error: class Base1 以標明爲 final
struct Base2 {
virtual void f() final;
};
struct Derived2 : Base2 {
void f(); // Error: Base2::f 以標明爲 final
};
六. 空指針
C++11 引入了新的關鍵字來代表空指針常數:nullptr
char* pc = nullptr; // OK
int * pi = nullptr; // OK
int i = nullptr; // error
foo(nullptr); // 呼叫 foo(char *)
這部分先介紹到這裏,C++11給程序員帶來了不少實惠,在這些新特性裏我最喜愛的依次是:右值引用,Lambda表達式,auto關鍵字,雖然現在項目組還在用VS2005開發程序,但我已經迫不及待地自己使用C++11了。
第二部分:標準庫的變更
這部分我想簡單提一下,具體寫出來不是一兩遍博客可以完成的,再說也沒必要,可以參考cppReference上詳細的說明。
1. 線程支持
標準庫提供了std::thread,還提供了線程同步的鎖(如std::mutex,std::recursive_mutex),可以 RAII 鎖 (std::lock_guard 和 std::unique_lock)。
2. 多元組
還記得boost庫中的tuple了嗎?現在已經引入到了C++11
// 多元組類別
std::tuple<int, std::string, float> Person(12, "amy", 30.1f);
int id = std::get<0>(Person);
std::string name = std::get<1>(Person); // "amy"
3. 正則表達式
4. 智能指針
std::shared_ptr不用說了,boost庫裏有的,unique_ptr我想和boost::scoped_ptr一樣使用就行了吧,頭文件<memory>
5. 包裝引用
std::ref,將值轉成引用