自從Cocos2d-x3.0開始,Cocos2dx就正式的使用了C++11標准.C++11簡潔方便的特性使程序的可拓展性和可維護性大大提高,也提高了代碼的書寫速度。
下面我們就來一起學習一下Cocos2d-x開發中那些不得不了解的C++11知識。
1.初始化列表
POD結構或者數組根據成員在結構內定義的順序,可以使用初始化列表來進行初始化以簡化代碼。
struct StructA{ int a; int b; }; StructA sa={1,2};
在C++03中,非POD結構的類或者STL容器並不支持這種簡便的寫法,而C++11提供了強大的支持。使用std::initializer_list可以讓類和普通函數使用初始化列表,並且STL容器也是可以使用初始化列表,代碼如下:
//類使用初始化列表 class ClassA{ public: ClassA(std::initializer_list<int>list){} }; ClassA a = { 1, 2, 3 }; /*注意!使用std::initializer_list需要先include <initializer_list>頭文件*/
//函數使用初始化列表 void func(std::initializer_list<float>list){ /*Function Body*/ } func({1.6f,2.8f});
/*注意!使用std::initializer_list需要先include <initializer_list>頭文件*/
//STL標准容器使用初始化列表 vector<string> s = {"hello","C++","11"};
可以看到在引入了std::initializer_list特性之后,初始化變量的工作簡潔了許多,非常方便。
2.自動類型推導
類型推導可以在編譯的時候自動來識別對象的類型,從而簡化代碼,更好的使用模版編程,使用auto關鍵字即可自動推導類型明確的變量,例如:
/*自動類型推導*/ vector<int> v; vector<int>::iterator it=v.begin(); //使用類型推導前 auto it2 = v.begin(); //使用類型推導后
decltype也可以根據已有的對象自動識別類型,但是它和auto的不同之處是:auto是自動推導出表達式右邊的類型,而decltype是自動推導出任意一個變量的類型,並且可以用該類型來定義變量,說起來比較難理解,看下面的代碼就一目了然了:
int num; decltype(num) b = 5;
3.自動范圍推導
在C++11以前,寫一個循環語句通常是這樣的:
for (int i = 0; i < 10; i++){ //使用自動范圍推導前 cout << i << endl; }
而在C++11中,for語句新增了范圍迭代的寫法,該寫法可以簡化for循環的代碼,“:”符號左邊是要編歷的元素類型,可以是引用或者const引用類型;而“:”右邊是要編歷的容器可以是數組或者STL容器等,代碼如下:
int arr []= { 1, 2, 3, 4, 5 }; //使用自動范圍推導后 for (int &i : arr){ cout << i << endl; }
4.智能指針和空指針
智能指針是一個類而並非是普通的指針,shared_ptr是一引用計數指針,一個shared_ptr只有在已經沒有任何其他shared_ptr指向其原本所指向的對象時,才會銷毀該對象。
除了shared_ptr之外,還有weak_ptr,但是weak_ptr並不擁有其所指向的對象,因此不影響該對象的銷毀與否,也不能對weak_ptr解引用,只能判斷該指針是否已經被銷毀。下面舉個例子說明一下shared_ptr:
/*智能指針和空指針*/ //智能指針只能被智能指針賦值,不能用shared_ptr<int> pq= new int; shared_ptr<int> p1(new int); //用{ }進入一個新的作用域 { //新的智能指針指向p1,這是相當於對int內存塊的一次retain shared_ptr<int> p2 = p1; *p2 = 123; //p2被銷毀,相當於對int內存塊的一次release,但是由於p1還指向該內存,引用計數器不為0,因此不會釋放 } return 0; //p1也被銷毀,此時引用計數為0,int所占用的內存被自動回收 /*注意!使用shared_ptr需要include <memory>*/
如果將share_ptr定義為類的成員變量,那么此智能指針的retain引用會在該對象被釋放的時候才釋放。
空指針nullptr的存在是為了解決NULL的二義性問題,因為NULL也可以代表0,nullptr的類型為nullptr_t,能隱式轉換為任何指針或者是成員指針的類型,也能和它們進行相等或者不等的比較。而nullptr不能隱式轉換為整數,也不能和整數做比較。
void foo(char *); void foo(int); foo(NULL); //調用的是void foo(int); foo(nullptr); void foo(char *);
5.Lambda特性
lambda表達式是一個非常好的新特性,當你需要在程序中添加一個新的臨時函數時,直接使用Lambda函數,會讓你感覺到原來寫程序還可以這么爽~(類似於Java中的 匿名內部類)。lambda的寫法如下:
[函數外部對象參數] (函數參數) -> 返回值類型{ 函數體}
(1)[ ]中的函數外部對象參數,允許在函數體內直接調用函數外部的參數;
(2)( )中的參數,同正常函數的參數沒有什么差異,是每次函數調用時傳入的變量;
(3)->后面跟着函數返回值的類型;
(4){ }里面可以編寫邏輯函數,並使用[ ]和( )傳入的參數
定義在lambda函數相同作用域的參數引用也可以被使用,這種參數集合一般被稱為閉包,[ ]中可以填寫下面的幾種類型的參數,將定義lambda函數作用域內的變量傳入函數體中。
1.[ ]可以沒有任何參數,這種情況下不傳入外部參數
2.[a,&b]傳入變量a的值以及變量b的引用
3.[&]以引用的方式傳入所有的變量
4.[=]以傳值的方式傳入所有的變量,值不可以被修改
5.[&,a]除了a用傳值的方式,其他變量都已引用的方式傳入
6.[=,&a]除了a用引用的方式傳入,其他變量都以傳值的方式傳入
下面讓我們通過一個例子來了解一下,當在lambda中使用了“=”傳入的參數,且對引用參數或者外部參數進行賦值操作之后,會產生意想不到的結果,並且還需注意在使用“&”時需要注意引用對象的生命周期。
/*Lambda表達式*/ int b, c, d; auto func0 = [&]()->void {b = 1; c = 2; d = 3; }; auto func1 = [=]()->int {return 2 * 3; }; auto func2 = [=, &b, &c]()->void {++b; c += d + b; }; auto func3 = [=]()->int {return b + d; }; func0(); //b,c,d分別為1,2,3 c=func1(); //c=6 func2(); //b=2;c=858993456,d=6; b = func3();//b=1717986916 return 0;
當Lambda被定義在類的成員函數中時,Lambda可以調用該類的private函數;當Lambda調用該類的成員函數時,操作成員變量或者其他成員函數時,需要將this傳入,=和&會傳入this。
使用std::function可以存儲Lambda函數,比如可以用function<void()>來存儲func0,用function<int()>來存儲func1,帶有參數的函數可以直接在()內輸入參數類型,在使用function時要包含頭文件functional。
#include <functional> function<void()> f1 = func0; function<int()>f2 = func1;
function還可以用於存放普通函數,靜態函數和類的公有成員函數,前兩者和lambda的用法一樣,直接將函數名賦值給function對象即可(無法識別重載的函數),但類的成員函數需要使用bind來綁定:
ClassA *obj = new ClassA(); function<void(int)> f2 = bind(&ClassA::memberFunc1,obj,std::placeholders::_1); function<void(int, char)>f3 = bind(&ClassA::memberFunc2,obj,std::placeholders::_2);
使用bind函數綁定成員函數和對象指針,使用std::placeholders占位符來表示函數的參數數量,其后綴依次從1~N。
6.顯式虛函數重載
override可以確保在重寫父類的虛函數,調整父類的虛函數時(改名字或者參數),不會忘記調整子類的虛函數。在編譯時,編譯器會為標記為override的虛函數檢查其父類是否有該虛函數:
class B{ public: virtual void virtuaalFunc(int); }; class C{ public: virtual void virtuaalFunc(int) override; //顯示重寫父類虛函數 virtual void virtuaalFunc(char) override; //錯誤 };
final可以保證子類不能重寫函數,不能具有相同簽名的函數,或者類不能被繼承。(類似於Java中final用法)override和final並不是C++11的關鍵字,只是在特定的位置才有特殊的含義,在其他地方仍然是當作變量來用的。
作者:馬三小伙兒
出處:http://www.cnblogs.com/msxh/p/5869992.html
請尊重別人的勞動成果,讓分享成為一種美德,歡迎轉載。另外,文章在表述和代碼方面如有不妥之處,歡迎
批評指正。留下你的腳印,歡迎評論!
