由於偶然的機會,在圖書館看到《深刻理解C++ 11:C++11新特性解析和應用》這本書,大體掃下,受益不淺,就果斷借出來,對於其中的部份內容進行詳讀並親自編程測試相關代碼,也就有了整理寫出這篇讀書筆記的基礎。C++做爲踏入編程的最初語言,一直充滿感情,而C++11做爲新標準雖然推出一段時間了,卻由於總總緣由直到如今纔去開始真正瞭解,不過一句話迴盪在腦中:當你認爲爲時已晚的時候,偏偏是最先的時候!從C++98到C++11, C++11標準經歷了10幾年的沉澱,以全新的姿態迎接新的挑戰,長話短說,進入正題,來一塊兒瞭解吧。(注:本筆記中全部代碼在Code::Blocks下編輯,使用gcc-4.9.3 -std=c++11模式下編譯)html
C++11的新基礎特性c++
1.1 用於兼容C99特性的宏,能夠檢查編譯系統對標準C庫的支持狀況,不過測試部分顯示未定義。程序員
cout<<"Standard Clib: "<< __STDC_HOSTED__<<endl; //Standard Clib: 1(指定編譯器目標系統是否包含完整的C庫) cout<<"Standard C: "<<__STDC__<<endl; //__STDC__:1(指定編譯器目標系統是否與C標準一致) //cout<<"C Standard Version: "<<__STDC_VERSION__<<endl; //測試gcc 沒有定義(支持標準C庫的版本) //cout<< "ISO/IEC: "<<__STDC_ISO_10646__<<endl; //測試gcc 沒有定義
1.2 __func__ 用於得到當前函數名字符串的宏編程
const char * Stanard_Macros(void) { //...... return __func__; //返回值:Stanard_Macros }
1.3 _Pragma 預處理操做符,與#pragma功能相同,不過由於支持傳遞字符串,因此能夠用宏命令替代,如對於常用的頭文件單次包含。數組
//頭文件防止重複包含 #pragma once; _Pragma("once"); #define PRAGMA(x) _Pragma(#x) //宏命令中使用#能夠實現替換字符串化 PRAGMA(once);
1.4 __VA_ARGS__ 變長參數的宏定義是指宏定義中參數列表的最後一個參數爲..., 而實現部分能夠用__VA_ARGS__替換ide
//__FILE__當前文件路徑 //__LINE__當前文本行 #define LOG(...){ \ fprintf(stderr, "%s: Line %d:\t", __FILE__, __LINE__);\ //輸出錯誤的文件及行號地址 fprintf(stderr, __VA_ARGS__);\ //輸出錯誤的數據 fprintf(stderr, "\n"); \ } LOG("%s", "NO ERR!"); // xxxx:xxxx: NO ERR!
1.5 新的整型long long/unsigned long long(長度不小於64位)函數
long long int lli = -90000000000LL; unsigned long long int ulli = 9000000000000ULL; cout<<"LLONG_MIN: "<<LLONG_MIN<<endl; //LLONG_MIN: -9223372036854775808 cout<<"LLONG_MAX: "<<LLONG_MAX<<endl; //LLONG_MAX: 9223372036854775807 cout<<"ULLONG_MAX: "<<ULLONG_MAX<<endl; //ULLONG_MAX: 18446744073709551615
1.6 斷言幫助開發者快速定位問題違反程序前提條件的錯誤,不過斷言只在程序運行時執行,這在某些狀況下,是不可接受的,特別是對於模板實例化時出現的錯誤,應該在編譯器就肯定。在C++11中引入了static_assert斷言來解決問題,它支持兩個參數輸入,一個是返回bool型的表達式,另外一個是警告信息。測試
static_assert(sizeof(b)==sizeof(a), "the parameters of bit_copy must have same width"); //靜態斷言,編譯器肯定,返回false則觸發告警信息
1.7 noexcept修飾符和noexcept操縱符是提供給庫做者使用,表示函數若是出現異常,不會拋出,編譯器會直接調用std::terminate()函數來終止程序運行,從而阻止了異常的的傳播和擴散。此外:C++11中類析構函數默認是noexcept(true),不過注意noexcept只會阻止異常的傳播和擴散(這對於定位錯誤頗有幫助),而不會阻止異常(如throw語句產生異常的)的發生。spa
const char * Stanard_Macros(void) noexcept //至關於noexcept(true), 若是有異常會發生,不會傳播和擴散,編譯會調用std::terminate()來終止程序運行 const char * Stanard_Macros(void) noexcept(false) //noexcept中能夠爲結果爲bool型的表達式
1.8 類成員變量的快速初始化和新的列表初始化,在C++11中,除了靜態變量,對於其它變量也容許使用等號或{}進行就地初始化。c++11
class Mem{ public: Mem(int i): m(i){}; ~Mem(){}; const char *ShowMem(void){ std::cout<<"Mem: "<<m<<" "; return __func__; }; private: int m{0}; }; //快速初始化 //初始化列表優於就地的列表初始化 class Init{ public: Init(): a(0){}; Init(int d): val('G'), a(d){}; //初始化列表實際效果後做用,效果上優於列表初始化 // ~Init(){ throw 1; }; ~Init(){}; const char * showPara(std::string str); int a; private: char val{'g'}; //成員變量的快速初始化 Mem mem{1}; std::string name{"Init"}; }; //C++的成員變量快速初始化 Init init1{5}; //C++11的統一列表初始化 Init init2; init1.showPara("init1: "); //init1: 5 init1: G name :Init Mem: 1 mem :ShowMem init2.showPara("init2: "); //init2: 0 init2: g name :Init Mem: 1 mem :ShowMem
1.9 非靜態成員的sizeof, sizeof做爲運算符,對於處理數組,類和對象時常常用到,不過在以前的C++98中,對於非靜態成員是不能直接編譯的,須要借用對像實例。
cout<<"Dyna Sizeof: "<<sizeof(((Init *)0)->a)<<endl; //C++98時借用實例對象得到非靜態成員長度 cout<<"Dyna Sizeof: "<<sizeof(Init::a)<<endl; //C++11支持直接得到
1.10 擴展的friend用法, friend時C++中比較特別的關鍵字,一方面它讓程序員省下了不少代碼,另外一方面也破壞了OOP中的封裝性,在C++11中作了改進,以保證更好的運用。
class People; template<typename T> class Ploy; template<typename T> class Ploy{ public: friend T; //指定類的友元類,容許友元類訪問本地參數 private: string m{"smart"}; }; class People{ public: void ShowPara(Ploy<People> *p){ cout<<"Ploy Data: "<<p->m<<endl; } }; //friend的擴展用法 Ploy<People> ppe; People Pe; Pe.ShowPara(&ppe);
1.11 final和override控制, final用來限制基類虛函數的對應的派生類不能重寫該虛函數,從而避免了某些接口被重寫覆蓋; override則指定了函數必須重載基類的虛函數,不然編譯通不過,這就避免了某些輸入名或者原型不匹配等錯誤的發生。
class MathObject{ public: virtual double Arith() = 0; virtual void Print() = 0; }; class Printable:public MathObject{ public: double Arith() = 0; //純虛函數僅容許爲0 void Print() final{ std::cout<<"Output is: "<< Arith() <<std::endl; } }; class Add2:public Printable{ public: Add2(double a, double b):x(a), y(b){} double Arith() override{ //override指定函數爲派生類的override(覆蓋)函數,會進行檢查 return x+y; } // void Print(){} //編譯會報錯,由於父類聲明瞭final,子類不容許重載 private: double x, y; }
1.12 默認的模板參數,C++11中模板和函數同樣,支持默認參數。
//類模板 C++98就容許,不過定義有要求 template<typename T1, typename T2 = int>class DefClass1{}; //容許,指定類模板的默認模板參數 //template<typename T1 = int, typename T2> class DefClass2; //通不過編譯,多個默認模板參數指定默認值時,必須遵照從右向作的原則 //函數模板 C++11添加 template<typename T1, typename T2 = int>void DefFunc1(T1 a, T2 b); template<typename T1 = int, typename T2>void DefFunc2(T1 a, T2 b); //容許
1.13 外部模板, 外部模板實現依賴於C++98已有的特性,顯示實例化。這樣就能夠實現一次實例,屢次使用。
//sundry.h template <typename T>void fun(T) {} //模板函數定義 //sundry1.cpp template void fun<int>(int); //模板實例化聲明 //sundry2.cpp extern template void fun<int>(int); //模板外部聲明
1.14 局部或者匿名類型作模板實參, C++11支持匿名或者局部類型做爲模板的實參,提供了更多的使用方法。
template<typename T> class X{}; template<typename T> void TempFunc(T t){}; struct {int i;}b; typedef struct{int i;}B; struct C{} c; X<B> x1; //匿名結構體做爲實參,不過只支持別名,不支持匿名結構體直接做爲實參 X<C> x2; //局部變量做爲實參 TempFunc(b); //匿名類型變量,C++11容許 TempFunc(c); //局部類型變量,C++11容許
到如今爲止,C++11的新基礎特性中比較重要的部分差很少講完了,從這些改動能夠看出,C++11向着更方便,更強大的方向穩步前進,並且這些改動只是滄海一粟,如新的lambda表達式,類的構造函數的新實現,更加常態化的SFINAE,這些都值得研讀,不過今天有點晚了,先到此爲止,後面我會一邊測試一邊總結,感謝C++11委員會,也感謝本書做者詳細的闡述,受用無窮!
參考資料: