C++ 11學習和掌握 ——《深刻理解C++ 11:C++11新特性解析和應用》讀書筆記(一)

  由於偶然的機會,在圖書館看到《深刻理解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 finaloverride控制, 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委員會,也感謝本書做者詳細的闡述,受用無窮!

  參考資料:

  《深刻理解C++11:C++ 11新特性解析與應用》

相關文章
相關標籤/搜索