在VS2012中可使用的C++11的新特性

     本文章轉載自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,將值轉成引用







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章