C++常物件和常成員函數詳解

2020-07-16 10:04:25
如果希望某個物件的值初始化以後就再也不被改變,則定義該物件時可以在前面加 const 關鍵字,使之成為常數物件(簡稱“常物件”)。例如:
class CDemo{
public:
    void SetValue(){ }
};
const CDemo Obj;  // Obj 是常數物件
在 Obj 被定義為常數物件的情況下,下面這條語句是錯誤的,編譯不能通過:
Obj.SetValue();
錯誤的原因是,常數物件一旦初始化後,其值就再也不能更改。因此,不能通過常數物件呼叫普通成員函數,因為普通成員函數在執行過程中有可能修改物件的值。

但是可以通過常數物件呼叫常數成員函數。所謂常數成員函數,就是在定義時加了 const 關鍵字的成員函數(宣告時也要加)。例如:
#include<iostream>
using namespace std;
class Sample{
   public:
   void GetValue() const;  //常成員函數
};
void Sample::GetValue() const  //常成員函數
{
}
int main(){
    const Sample o;
    o.GetValue();  //常數物件上可以執行常數成員函數
    return 0;
}
上面的程式在 Visual Studio 中沒有問題,但在 Dev C++ 中,要為 Sample 類編寫無參建構函式才可以。在 Dev C++ 中,常數物件如果使用無參建構函式初始化,就需要顯式寫出無參建構函式。

常數物件上可以執行常數成員函數,是因為常數成員函數確保不會修改任何非靜態成員變數的值。編譯器如果發現常數成員函數內出現了有可能修改非靜態成員變數的語句,就會報錯。因此,常數成員函數內部也不允許呼叫同類的其他非常數成員函數(靜態成員函數除外)。

思考題:為什麼上面一段話要強調“非靜態成員變數”和“靜態成員函數除外”?

兩個成員函數的名字和參數列相同,但一個是 const 的,一個不是,則它們算過載。例如:
#include <iostream>
using namespace std;
class CTest{
private:
    int n;
public:
    CTest(){n = 1;}
    int GetValue() const { return n; }
    int GetValue() { return 2*n; }
};
int main(){
    const CTest objTestl ;
    CTest objTest2;
    cout << objTestl.GetValue () << "," << objTest2.GetValue();
    return 0;
}
程式的輸出結果是:
1, 2

可以看到,通過常數物件呼叫 GetValue 函數,那麼被呼叫的就是帶 const 關鍵字的 GetValue 函數;通過普通物件呼叫 GetValue 函數,被呼叫的就是不帶 const 關鍵字的 GetValue 函數。

基本上,如果一個成員函數中沒有呼叫非常數成員函數,也沒有修改成員變數的值,那麼將其寫成常數成員函數是好的習慣。