C++程式設計思想重點筆記(上)

Alexia(minmin)發表於2014-12-01
  1. C和C++指標的最重要的區別在於:C++是一種型別要求更強的語言。void *而言,這一點表現得更加突出。C雖然不允許隨便地把一個型別的指標指派給另一個型別,但允許通過void *來實現。例如:

    
    
     bird* b;
     rock* r;
     void* v;
     v = r;
     b = v;
    
    

    C++不允許這樣做,其編譯器將會給出一個出錯資訊。如果真的想這樣做,必須顯式地使用對映,通知編譯器和讀者。

  2. 引數傳遞準則
    當給函式傳遞引數時,人們習慣上應該是通過常量引用來傳遞,這種簡單習慣可以大大提高效率:傳值方式需要呼叫建構函式和解構函式,然而如果不想改變引數,則可通過常量引用傳遞,它僅需要將地址壓棧。 事實上,只有一種情況不適合用傳遞地址方式,這就是當傳值是唯一安全的途徑,否則將會破壞物件(而不是修改外部物件,這不是呼叫者通常期望的)。

  3. C++訪問許可權控制:public、private、protected
    其中protected只有在繼承中才有不同含義,否則與private相同,也就是說兩者只有一點不同:繼承的結構可以訪問protected成員,但不能訪問private成員。

  4. 前置宣告注意

    
    
     struct X;  // Declaration(incomplete type spec)
     struct Y
     {
       void f(X *memx);  
       void g(X memx);  // not allowed, the size of X is unknown.
     };
    
    

    這裡f(X*)引用了一個X物件的地址,這是沒有任何問題的,但如果是void g(X memx);就不行了,編譯器會報錯。這一點很關鍵,因為編譯器知道如何傳遞一個地址,這一地址大小是一定的,而不用管被傳遞的物件型別大小。如果試圖傳遞整個物件,編譯器就必須知道X的全部定義以確定它的大小以及如何傳遞它,這就使程式設計師無法宣告一個類似於Y :: g(X) 的函式。

  5. C++是純的嗎?
    如果某個類的一個函式被宣告為friend,就意味著它不是這個類的成員函式,但卻可以修改類的私有成員, 而且它必須被列在類的定義中,因此我們可以認為它是一個特權函式。這種類的定義提供了有關許可權的資訊,我們可以知道哪些函式可以改變類的私有部分。 因此,C++不是完全的面嚮物件語言,它只是一個混合產品。friend關鍵字就是用來解決部分的突發問題。它也說明了這種語言是不純的。畢竟C + +語言的設計是為了實用,而不是追求理想的抽象。

  6. C++輸入輸出流的操縱運算元(manipulator)有:endl、flush、ws、hex等。

    
    
     cout<<flush;   // 清空流   
     cout << hex << "0x" << i;  // 輸出16進位制   
     cin>>ws;  // 跳過空格
    
    

    iostream.h還包括以下的操縱運算元:

    如何建立我們自己的操縱運算元?
    我們可能想建立自己的操縱運算元,這是相當簡單的。一個像endl這樣的不帶引數的操縱運算元只是一個函式,這個函式把一個ostream引用作為它的引數。對endl的宣告是:

    
    
    ostream& endl(ostream&);
    
    

    例子:產生一個換行而不重新整理這個流。人們認為nl比使用endl要好,因為後者總是清空輸出流,這可能引起執行故障。

    
    
     ostream& nl(ostream& os) {
       return os << "\n";
     }
     int main() {
       cout << "newlines" << nl << "between" << nl << "each" << nl << "word" << nl;
      return 0;
     }
    
    
  7. C語言中const與C++中const的區別:
    常量引進是在早期的C++版本中,當時標準C規範正在制訂。那時,常量被看作是一個好的思想而被包含在C中。但是,C中的const意思是“一個不能被改變的普通變數”,在C中,它總是佔用儲存而且它的名字是全域性符。C編譯器不能把const看成一個編譯期間的常量。在C中, 如果寫:

    
    
    const bufsize=100char buf[bufsize];
    
    

    儘管看起來好像做了一件合理的事,但這將得到一個錯誤結果。因為bufsize佔用儲存的某個地方,所以C編譯器不知道它在編譯時的值。在C語言中可以選擇這樣書寫:

    
    
    const bufsize;
    
    

    這樣寫在C++中是不對的,而C編譯器則把它作為一個宣告,這個宣告指明在別的地方有儲存分配。因為C預設const是外部連線的,C++預設cosnt是內部連線的,這樣,如果在C++中想完成與C中同樣的事情,必須用extern把連線改成外部連線:

    
    
    extern const bufsize;//declaration only
    
    

    這種方法也可用在C語言中。
    注意:在C語言中使用限定符const不是很有用,即使是在常數表示式裡(必須在編譯期間被求出);想使用一個已命名的值,使用const也不是很有用的。C迫使程式設計師在前處理器裡使用#define。

  8. 類裡的const和enum
    下面的寫法有什麼問題嗎?:

    
    
     class bob {
         const size = 100;  // illegal
         int array[size];   // illegal
     }
    
    

    結果當然是編譯不通過。why?因為const在類物件裡進行了儲存空間分配,編譯器不能知道const的內容是什麼,所以不能把它用作編譯期間的常量。這意味著對於類裡的常數表示式來說,const就像它在C中一樣沒有作用。

    在類裡的const意思是“在這個特定物件的壽命期內,而不是對於整個類來說,這個值是不變的”。那麼怎樣建立一個可以用在常數表示式裡的類常量呢?
    一個普通的辦法是使用一個不帶例項的無標記的enum。列舉的所有值必須在編譯時建立,它對類來說是區域性的,但常數表示式能得到它的值,這樣,我們一般會看到:

    
    
     class bob {
         enum { size = 100 };  // legal
         int array[size];      // legal
     }
    
    

    使用enum是不會佔用物件中的儲存空間的,列舉常量在編譯時被全部求值。我們也可以明確地建立列舉常量的值:enum { one=1,two=2,three};

  9. 類裡面的const成員函式

    
    
     class X {
         int i;
     public:
         int f() const;      
     }
    
    

    這裡f()是const成員函式,表示只能const類物件呼叫這個函式(const物件不能呼叫非const成員函式),如果我們改變物件中的任何一個成員或呼叫一個非const成員函式,編譯器將發出一個出錯資訊。
    關鍵字const必須用同樣的方式重複出現在定義裡,否則編譯器把它看成一個不同的函式:

    
    
    int X::f() const { return i;}

    任何不修改成員資料的函式應該宣告為const函式,這樣它可以由const物件使用。
    注意:建構函式和解構函式都不是const成員函式,因為它們在初始化和清理時,總是對物件作些修改。


    引申:如何在const成員函式裡修改成員 —— 按位和與按成員const

    如果我們想要建立一個const成員函式,但仍然想在物件裡改變某些資料,這時該怎麼辦呢?這關係到按位const和按成員const的區別。按位const意思是物件中的每個位是固定的,所以物件的每個位映像從不改變。按成員const意思是,雖然整個物件從概念上講是不變的,但是某個成員可能有變化。當編譯器被告知一個物件是const物件時,它將保護這個物件。

    這裡我們要介紹在const成員函式裡改變資料成員的兩種方法。

  • 第一種方法已成為過去,稱為“強制轉換const”。它以相當奇怪的方式執行。取this(這個關鍵字產生當前物件的地址)並把它強制轉換成指向當前型別物件的指標。看來this已經是我們所需的指標,但它是一個const指標,所以,還應把它強制轉換成一個普通指標,這樣就可以在運算中去掉常量性。下面是一個例子:

    class Y {
     int i, j;
    public:
     Y() { i = j = 0; }
     void f() const;
    };
    
    void Y::f() const {
    //!  i++;  // error
       ((Y*)this)->j++;  // ok , cast away const feature.
    }

    這種方法可行,在過去的程式程式碼裡可以看到這種用法,但這不是首選的技術。問題是:this沒有用const修飾,這在一個物件的成員函式裡被隱藏,這樣,如果使用者不能見到原始碼(並找到用這種方法的地方),就不知道發生了什麼。

  • 第二種方法也是推薦的方法,就是在類宣告裡使用關鍵字mutable,以指定一個特定的資料成員可以在一個const物件裡被改變。

    class Y {
     int i;
     mutable int j;
    public:
     Y() { i = j = 0; }
     void f() const;
    };
    
    void Y::f() const {
    //!  i++;  // error
       ((Y*)this)->j++;  // ok , mutable.
    }

     

  • volatile關鍵字

    volatile的語法與const是一樣的,但是volatile的意思是“在編譯器認識的範圍外,這個資料可以被改變”。不知何故,環境正在改變資料(可能通過多工處理),所以,volatile告訴編譯器不要擅自做出有關資料的任何假定—在優化期間這是特別重要的。如果編譯器說:“我已經把資料讀進暫存器,而且再沒有與暫存器接觸”。一般情況下,它不需要再讀這個資料。但是,如果資料是volatile修飾的,編譯器不能作出這樣的假定,因為可能被其他程式改變了, 它必須重讀這個資料而不是優化這個程式碼。

    注意:

    • 就像建立const物件一樣,程式設計師也可以建立volatile物件,甚至還可以建立const volatile對 象,這個物件不能被程式設計師改變,但可通過外面的工具改變。
    • 就像const一樣,我們可以對資料成員、成員函式和物件本身使用volatile,可以並且也只能為volatile物件呼叫volatile成員函式。
    • volatile的語法與const是一樣的,所以經常把它們倆放在一起討論。為表示可以選擇兩個中的任何一個,它們倆通稱為c-v限定詞

  • 相關文章