1. 程式人生 > >為什麼const物件只能呼叫const成員函式,而不能呼叫非const成員函式?

為什麼const物件只能呼叫const成員函式,而不能呼叫非const成員函式?

參考:http://www.cnblogs.com/cplinux/articles/5553716.html

http://blog.csdn.net/lanxuezaipiao/article/details/41647333#comments

在c++中,我們可以用const來定義一個const物件,const物件是不可以呼叫類中的非const成員函式,這是為什麼呢?下面是總結的一些原理。

假設有一個類,名字為test程式碼如下:

複製程式碼
1 class test{
2   int i;
3 public:
4   void print();
5   test(int i);
6 };
複製程式碼

我們知道c++在類的成員函式中還會隱式傳入一個指向當前物件的this指標,所以在test類中,實際的print函式應該是這樣的void print(test * this);,這代表一個指向test物件的指標this被傳入到了print函式中

假如現在我們用test類建立一個物件,

1 test obj1(12);
2 obj1.print();

第二句,obj1.print();其實相當於print(&obj1);,即把當前的物件obj1的指標傳遞到print()函式,這是沒問題的

如果用test類建立一個const物件,然後去呼叫print()函式呢?這就會出現問題

const test obj2(122);
obj2.print();

這時obj2物件的指標就會傳遞給test *this 指標,而obj2的地址翻譯成指標型別應該是這樣的,const test* this,即這時會出現型別不匹配的錯誤,在visual studio 中會出現類似於下面的錯誤:

4

所以通過上面的說明,我們知道了為什麼const 物件不能呼叫非const成員函式。

下面解釋為什麼const 物件可以呼叫const成員函式,

1 class test{
2 public:
3      void print()const;
4 };

前面我們把非const成員函式print(),翻譯了一下,同樣const成員函式也要翻譯,void print()const; 可以翻譯成 void print(const test* this);,那麼常量物件的地址翻譯是const test* this; 是和void print() const;中this指標的型別是一樣的,所以常量物件可以呼叫const成員函式。

有一個點要注意,在c++中其實是有最小許可權原則的,非const物件是可以呼叫const成員函式的。

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

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

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

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

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

    這種方法可行,在過去的程式程式碼裡可以看到這種用法,但這不是首選的技術。問題是: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.
}
  • 第二種方法也是推薦的方法,就是在類聲明裡使用關鍵字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.
}