標籤:ble 結構 枚舉 範圍 turn 操作符重載 不能 pointer exp
1.auto
它的功能為類型推斷。auto是一個類型的預留位置,通知編譯器去根據初始化代碼推斷所聲明變數的真實類型。各種範圍內聲明變數都可以用到它。
auto i=42; //i is an intauto l=42LL; //l is an long longauto p=new foo(); //p is a foo
在遍曆STL容器時需要聲明迭代器(iterator),現在不需要聲明typedef就可以得到簡潔的代碼了。
std::map<std::string,std::vector<int>> map;for( auto it = begin(map); it != end(map); ++it ){}
需要注意的是,auto不能用來聲明函數的傳回值,但如果函數有一個尾隨的傳回型別時,auto可以出現在函式宣告中傳回值位置。在這種情況下,auto並不是告訴編譯器去推斷傳回型別,而是指引編譯器去函數的末端尋找傳回值類型。
template< typename T1, typename T2>auto compose( T1 t1, T2 t2) -> decltype( t1 + t2 ){ return t1+t2;}auto v = compose( 2, 3.14); //v‘s type is double2.nullptr
以前都是用0來表示null 指標的,但由於0可以被隱式類型轉換為整型,這就出現了一些問題。
void f( int );void f( char * p );
如果存在以上兩種方法,調用f(0)時,C++98編譯失敗,但在C++11中調用的是f(int)方法;想要調用f(char *p)調用方式為:f( nullptr )。
nullptr和任何指標類型以及類成員指標類型的空值之間可以發生隱式類型轉換,同樣也可以隱式轉換為bool型(取值為false)。但是不存在到整型的隱式類型轉換。
為了向前相容,0仍然是個合法的空值指標。
3.Rang-based for loops(基於範圍的for迴圈)
思考問題:對vector<int>中的每個元素加1。
void add( int & a){ a += 1;}int intArray[] = {1,2,3,4,5};vector< int > intVector(intArray,intArray+5);
第一種原始做法:
for(vector<int>::iterator iter = intVector.begin(); iter != intVector.end(); iter++ ){ add(*iter);}
第二種使用boost的foreach:
#include <boost/foreach.hpp>BOOST_FOREACH(int& a,intVector) { add(a);}
第三種使用for_each:
#include <algorithm>for_each(intVector.begin(),intVector.end(),add);
第四種就是C++11擴充的for語句:
for( int& e : intArray ) { e = e+1;}
用這種新的寫法可以遍曆數組、初始化列表以及任何重載了的非成員的begin和end函數的類型。
4.Override和final
vitual關鍵字是可選的,這使得閱讀代碼變得很費勁。因為可能需要追溯到繼承體系的源頭才能確定某個方法是否是虛函數。為了增加可讀性,我總是在衍生類別裡也寫上virtual關鍵字。即使這樣,仍然會產生一些微妙的錯誤。所以C++11加入了兩個新的標識符。
override標識符:指定在基類中的虛函數應該被重寫。
final標識符:指定衍生類別中的函數不會重寫基類中的虛函數。
class B {public: virtual void f(int) {std::cout << "B::f" << std::endl;}};class D : public B{public: virtual void f(int)override final {std::cout << "D::f" << std::endl;}};class F : public D{public: virtual void f(int) {std::cout << "F::f" << std::endl;}};
上述程式會報錯:“D::f”聲明為“final”的函數無法被“F::f”重寫。
5.Stong-typed enums 強型別枚舉
傳統的C++枚舉類型存在一些缺陷:它們會將枚舉常量暴露在外層範圍中(這可能導致名字衝突,如果同一個範圍中存在兩個不同的枚舉類型,但是具有相同的枚舉常量就會衝突),而且它們會被隱式轉換為整型,無法擁有特定的使用者定義型別。
enum A { a, b }; //預設從0開始賦值,依次加1//enum B { a, c }; //報錯a重定義:以前的定義為枚舉數
cout << a << "," << b ; //輸出為0,1
在C++11中通過引入了一個稱謂強型別枚舉的新類型,修正了這種情況,強型別枚舉由關鍵字enum class標識。它不會將枚舉常量暴露到外層範圍中,也不會隱式的轉換為整型,並且擁有使用者指定的特定類型。
enum class A { a, b};enum class B { a, c}; //不會報錯//cout << a <<endl; //報錯:a未聲明的標識符//cout << A::a <<endl; //報錯:<<沒有找到接受A類型的右操作的運算子cout << (int)A::a <<endl; //輸出06.Smart Pointers 智能指標
1)unique_ptr:如果記憶體資源的所有權不需要共用,就用這個(它沒有拷貝建構函式),但是它可以轉讓給另一個unique_ptr(存在move建構函式)。
void foo(int* p){ std::cout << *p << std::endl;}std::unique_ptr<int> p1(new int(42)); // 聲明一個智能指標p1,初始化值為42std::cout << * p1 << std::endl; // 輸出42std::unique_ptr<int> p2 = std::move(p1); // p1轉讓給p2,p1變為空白,自動釋放記憶體//std::cout << * p1 << std::endl; // 出錯:程式被中斷,因為p1已被置為空白std::cout << p1.get() << std::endl; // 輸出為00000000,get函數返回的是nullptrstd::cout << * p2 << std::endl; // 輸出42(*p2)++; // p2指向內容加1if(p2) foo(p2.get()); // 輸出43
2)shared_ptr:如果記憶體資源需要共用,那麼使用這個。
void foo( int * p ){ cout << *p <<endl;}void bar( std::shared_ptr< int > p){ ++(*p);}std::shared_ptr< int > p1( new int(42) ); //聲明一個智能指標p2,初始化為42std::shared_ptr< int > p2 = p1; //把p1共用給p2bar( p1 ); //改變p1的值,同時也在改變p2的值foo( p2.get() ); //輸出為43
其中第一個聲明也可以寫成:
auto p3 = std::make_shared<int>(42);
make_shared<T>是一個非成員函數,使用它的好處是可以一次性分配共用對象和智能指標自身的記憶體。而顯示地使用shared_ptr建構函式來構造至少需要兩次記憶體配置。除了會產生額外的開銷,還可能會導致記憶體流失。在下面的例子中,seed()拋出一個錯誤就會產生記憶體流失。
void foo( std::shared_ptr<int> p,int init ){ * p = init; }foo( std::shared_ptr<int>(new int(42)),seed());
3)weak_ptr:持有被shared_ptr所管理對象的引用,但是不會改變引用計數值。它被用來打破依賴迴圈(想象有一個tree結構中,父節點通過一個共用所有權的引用(chared_ptr)引用子節點,同時子節點又必須持有父節點的引用。如果這兩個引用也共用所有權,就會導致一個迴圈,最終兩個節點記憶體都無法釋放)。
auto p = std::make_shared<int>(42);std::weak_ptr<int> wp = p; //wp只是獲得了觀測權,沒有共用資源 auto sp = wp.lock(); //lock()從觀測的shared_ptr獲得一個可用的shared_ptr對象std::cout << *sp << std::endl; p.reset(); //reset()銷毀函數 if(wp.expired()) //expired()==true表示wp所指對象已經銷毀 std::cout << "expired" << std::endl;
7.Lambdas(匿名函數)
文法形式:[函數對象參數](操作符重載函數參數) mutable或exception聲明 -> 傳回值類型{函數體}。
1)函數對象參數有以下幾種形式:
空:沒有使用任何函數對象參數。
=:函數體內可以使用Lambda所在作用範圍內所有可見的局部變數,並且是值傳遞方式。
&:函數體內可以使用Lambda所在作用範圍內所有可見的局部變數,並且是引用傳遞方式。
this:函數體內可以使用Lambda所在類中的成員變數。
a:將a按值進行傳遞。按值進行傳遞時,函數體內不能修改傳遞進來的a的拷貝,因為預設情況下函數是const的,要修改傳遞進來的a的拷貝,可以添加mutable修飾符。
&a:將a按引用進行傳遞。
a,&b:將a按值進行傳遞,b按引用進行傳遞。
=,&a,&b:除a和b按引用進行傳遞外,其他參數都按值進行傳遞。
&,a,b:除a和b按值進行傳遞外,其他參數都按引用進行傳遞。
2)操作符重載函數參數:沒有參數時,這部分可以省略。 參數可以通過按值和按引用兩種方試進行傳遞。
3)mutable或exception聲明:這部分可以省略。按值傳遞函數對象參數時,加上mutable修飾符後,可以修改按值傳遞進來的拷貝。exception聲明用於指定函數拋出的異常,如拋出整數類型的異常,可以使用throw(int)。
4)->傳回值類型:標識函數傳回值的類型。當傳回值為void,或者函數體中只有一處return的地方,這部分可以省略。
5)函數體:標識函數的實現,這部分不能省略,但函數體可以為空白。
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;}); // 輸出6 5 4 3 2for_each(iv.begin(), iv.end(), [=](int &x){x *= (a + b);}); // iv每個數乘以3
for_each(iv.begin(),iv.end(),[](int &x){cout<<x<<endl;}); //輸出15 12 9 6 3for_each(iv.begin(), iv.end(), [=](int &x)->int{return x * (a + b);}); //只是返回每個數的3倍,但是不改變原來數組的數
for_each(iv.begin().iv.end(),[](int &x){cout<<x<<endl;}); //輸出為15 12 9 6
再舉一個例子:
std::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3); std::for_each(std::begin(v), std::end(v), [](int n) {std::cout << n << std::endl;}); //輸出為1 2 3 auto is_odd = [](int n) {return n%2==1;}; //定義了一個函數is_odd,返回1(不能夠被2整數)或0(能被2整數)auto pos = std::find_if(std::begin(v), std::end(v), is_odd); //find_if尋找符和條件的資料,返回指向資料的指標if(pos != std::end(v))std::cout << *pos << std::endl; //第一個合格資料為1,輸出為1
更複雜的是遞迴lambda。
std::function<int(int)> lfib = [&lfib](int n) {return n < 2 ? 1 : lfib(n-1) + lfib(n-2);};cout<<lfib(5)<<endl;;
(暫時更新到這裡,後續會繼續更新)
C++11新特性-常用