天天看點

[C++學習筆記02]從C到C++

  1. bool類型

      取值:true,false。

      大小:VC占1個位元組。

  2. const限定符

      常量(常變量)聲明形式:

        const 資料類型 常量名=常量值;     資料類型 const 常量名=常量值;

        注:上面兩者等價。

      例子說明:

    #include <iostream>
    using namespace std;
    
    int main(void)
    {
        //const int a;            Error,常量必須初始化
        const int a = 100;        
        //a = 200;                Error,常量不能重新被指派
    
        int b = 22;
        const int * p;             //const在*左邊,表示*p為常量,經由*p不能更改指針所指向的内容
        p = &b;
        //*p = 200;                Error,常量不能重新被指派
        //int * const p2;          Error,p2為常量,常量必須初始化
        int * const p2 = &b;       //const在*右邊,表示p2為常量
        //int c =100;
        //p2 = &c;                 Error,常量不能重新被指派
        *p2 = 200;
    
        cout<<b<<endl;
    
        return 0;
    }      
  3. const與#define

      const定義的常量與#define定義的符号常量的差別:

        前者有類型,要進行類型檢查,後者隻是單純的替換;

        前者在編譯期配置設定記憶體,後者在預處理期替換,不配置設定記憶體;

        作用域不同,前者的作用域與一般的變量的作用域相同,後者為定義處到程式結束,可以使用#undef取消。

      #define容易産生副作用

    //Effective C++ 3rd的一個例子。
    #define CALL_WITH_MAX(a,b) f((a) > (b) ? (a) : (b))
    
    int a = 5;
    int b = 0;
    CALL_WITH_MAX(++a, b);    //a被累加二次
    CALL_WITH_MAX(++a, b+10);    //a被累加一次
    
    // 在這裡,調用f之前,a的遞增次數竟然取決于“它被拿來和誰比較”      
      #define中#,##的使用
    #include <iostream>
    using namespace std;
    
    //#define STR(a) a  // 不使用#,使用STR(a),a未聲明,會報錯。
    #define STR(a) #a // 字元串化,當這裡使用#,你使用STR(a),a未聲明,會被轉化成字元串"a"。
    #define CAT(a, b) a##b // 連接配接作用
    
    int main()
    {
        int ab = 100;
        const char *str = STR(a);
        cout <<str << endl; //"a"
        cout << CAT(a, b) << endl; // 100
    
        return 0;
    }      
  4. 結構體對齊

    什麼是結構體對齊?

        編譯器為每個“資料單元”按排在某個合适的位置上。     C、C++語言非常靈活,它允許你幹涉“記憶體對齊”。

      為什麼要對齊?

        性能原因:在對齊的位址上通路資料快。

      對齊方法

         1.第一個變量與結構體變量的偏移量為0;    

        2.計算每個變量的對齊數(編譯器設定的對齊數與該成員大小的較小值);

        3.每個成員變量 的位址為:對齊數的整數倍;對其到對齊數整數倍的位址;

        4.結構體總大小為最大對齊數的整數倍。

      對齊案例

    // vc++預設對其整數為8,g++預設為4
    // 在屬性->C/C++->代碼生成可以修改
    // 或者 #pragma pack(8)修改
    // #pragma pack()取消修改
    /*
    struct MyStruct {
        char ch; // 0
        double db; // 8 
        int it; // 16-23
    }; // 24*/
    
    /*
    struct MyStruct {
        char ch; // 0
        int it; // 4
        double db; // 8-15
    }; // 16*/
    
    /*
    struct MyStruct {
        char ch1; // 0
        int it; // 4
        char ch2; // 8
        double db; // 16-23
    }; // 24*/
    
    struct MyStruct {
        double db1; //0
        char ch1; //8
        float ft; //12
        int it; // 16
        char ch2; // 20
        double db2; // 24-31
    }; // 32      
  5. 域運算符   作用:     用于對與局部變量同名的全局變量進行通路;     用于表示類的成員。
  6. new、delete運算符

      new文法:

        指針變量=new 資料類型(初始化參數-可選);

        指針變量=new 資料類型[長度n];

      delete文法:

        delete 指針變量;

        delete [] 指針變量;

      new一個新對象過程:

        記憶體配置設定(operator new)

        調用構造函數

      delete一個對象過程:

        調用析構函數

        釋放記憶體(operator delete)

      operator new/new operator/placement new:

        operator new:隻配置設定記憶體

        new operator:調用構造函數 + 配置設定記憶體

        placement new:不配置設定記憶體,調用拷貝構造函數

  7. 重載

      函數重載:又稱為函數的多态(靜态多态),編譯時期指定函數入口位址,靜态聯編。

        成立條件:

          形參數量不同;       形參類型不同;       形參的順序不同;       形參數量和形參類型都不同。

           注:函數重載跟函數的傳回值無關。   派生類與基類通過虛函數實作的多态(動态多态),運作時期指定函數入口位址,動态聯編。

  8. name managling與extern “C”

      name managling:就是将函數的名字修改,實作函數重載。

      extern "C"使用:

    #ifdef __cpluscplus
    extern “C”
    {
    #endif
    ...
    #ifdef __cpluscplus
    }
    #endif      
  9. 帶預設參數的函數

      幾點說明:

        1.函數既有定義又有聲明時,聲明時指定後,定義後就不能再指定預設值;

        2.預設值的定義必須遵守從右到左的順序,如果某個形參沒有預設值,則它左邊的參數就不能有預設值;

        3.函數調用時,實參與形參按從左到右的順序進行比對

  10. 引用

      幾點說明:

        1.引用在定義的時候要進行初始化;

        2.引用一經初始化,不能重新引用其他變量;

        3.非const引用不能引用const常量;

        4.double val3 = 3.14;const int& ref4 = val3;隻是報警告,等價于定義一個臨時變量int tmp = 3,然後讓ref4去引用tmp,當ref4不是常引用的時候,編譯器直接報類型轉換錯誤(強類型)。

        5.同理4,const int &ref = 10;常引用使用字面量初始化自然就是有效的。

        6.不能傳回對局部變量的引用,通過傳回引用,可以讓函數調用作為左值,改變全局或者靜态變量的值(通常在函數中傳回靜态或者全局變量的引用)。

        7.教課書上說的是引用不配置設定記憶體空間,但是不是與引用是使用指針實作的沖突麼?

  11. 内聯函數

      幾點說明:    

        1.不能有遞歸    

        2.不能包含靜态資料    

        3.不能包含循環    

        4.不能包含switch和goto語句    

        5.不能包含數組    

        注:若一個内聯函數定義不滿足以上限制,則編譯系統把它當作普通函數對待。

      内聯函數與帶參數宏差別

        一樣是内聯函數要進行類型檢查,宏隻是簡單的替換。

  12. 類型轉換

      舊式轉換

        (T)expr     T(expr)   新式轉換

        const_cast<T>(expr)

    void change(const int &r)
    {
        int &rr = const_cast<int &>(r);
        rr = 200;
    }
    
    void test(int &r)
    {
        r = 100;
    }
    
    int main(void)
    {
        int n = 100;
        const int *pn = &n;
        const int &rn = n;
    
        //const_cast一般用于指針或者引用
        // 個人覺得const_cast應該應用到該場景
        // 即變量本身是變量,但是被常量指針或者常引用引用了
        // 希望通過指針,或者引用去修改最初變量的值
        int *p = const_cast<int *>(pn);
        *p = 200;
        cout << n << endl; // 200 
        int &r = const_cast<int &>(rn);
        r = 300;
        cout << r << endl;
    
        const int val = 100; // 常量本身是不能更改的,最多你能更改它在記憶體中的值,當使用該常量時,它去常量表直接取值
        // int varl = const_cast<int>(val); //Error, 無法從“const int”轉換為“int”
    
        int m = 10;
        change(m);
        cout << m << endl; // 200
    
        //使用const_cast去除const限定的目的不是為了修改它的内容
        //使用const_cast去除const限定,通常是為了函數能夠接受這個實際參數
        const int a = 10;
        //test(a); // Error, 從“const int”轉換為“int &”
        test(const_cast<int&>(a)); // 隻是修改了a在記憶體中的值,a本身的值不會修改,在常量表中
    
        return 0;
    }      

        static_cast<T>(expr)

          了解成一般的強制轉換即可。

        reinterpret_cast<T>(expr)

          “通常為操作數的位模式提供較低層的重新解釋”也就是說将資料以二進制存在形式的重新解釋。

    int main(void)
    {
        int i;
        char *p = "This is a example.";
        i = reinterpret_cast<int>(p);
        //此時結果,i與p的值是完全相同的。
    
        //cout << p << endl; // This is a example.
        cout << i << endl; // 13032560,cout 見int型,那麼直接相當于printf的%d輸出了他的位址
        printf("%s\n", i); // This is a example.
        printf("%d\n", i); // 13032560
        printf("%s\n", p); // This is a example.
        printf("%d\n", p); // 13032560
    
        int m = 10;
        int *ip = &m;
        char *pc = reinterpret_cast<char*>(ip);
        // 程式員需要記得pc所指向的真實對象是int型,并非字元串。
        // 如果将pc當作字元指針進行操作,可能會造成運作時錯誤
        // 如int len = strlen(pc);
    
        printf("%d\n", *pc); // 10
    
        return 0;
    }      

        dynamic_cast<T>(expr)

          執行“安全向下”轉型操作,也就是說支援運作時識别指針或所指向的對象,這是唯一個無法用舊式語來進行的轉型操作。

轉載于:https://www.cnblogs.com/ifpelset/articles/4505648.html