编程菜鸟,在学习c++多态性的过程中,写了这样的测试代码,这里会有类型限定符不兼容。当我在抽象基类的函数声明后添加上const后,就不会出现类型不兼容,这是为什么?
认证0级讲师
const限定的變數以及其引用只能呼叫const限定的成員函數const限定代表變數的值不會改變,而要想不改變變數的值則只能呼叫const修飾的成員函數
你的問題其實與多態無關,要解答你這個問題需要兩個方面的知識點。
this
首先,類別的成員函數內要存取類別的成員,需要透過this指標存取(名稱不衝突時可省略),透過一個物件呼叫成員函數的時候會將物件本身透過this指標傳遞給成員函數。例如:
class A { public: explicit A(int xx = 0) : x(xx) {} int get() { return x; } void set(int xx) { x = xx; } private: int x; }; int main() { A a; a.set(10); // a.x = 10 int i = a.get(); // i = 10 }
為什麼我能在a.get()函數裡存取物件a的成員x?就是透過這個this指針。在透過a呼叫a.get()的時候,編譯器會把物件a的位址賦值給this指針,這裡的this指標的型別是A *const。而在get()函數內,return x;這個語句其實相當於return this->x;,這樣我們就能夠透過a.get()來存取物件a的成員x了。
a.get()
a
x
A *const
get()
return x;
return this->x;
同理,為什麼a.set(10);能夠把物件a的成員x賦值成10?也是透過this指針。 x = xx;就相當於this->x = xx;。
a.set(10);
x = xx;
this->x = xx;
以上兩種情況下,由於x的解析不可能出現歧義,所以this指標可以省略。但是,如果我想在成員函數set的參數裡使用與成員x相同的變數名稱該怎麼辦?依然透過this指針進行區分。我們可以把成員函數set的定義修改如下:
set
A::void set(int x) { this->x = x; }
這裡的this指標就不能省略了,否則的話,寫成x = x;就會出現歧義,編譯器無法解析x到底是函數的參數還是物件的成員。
x = x;
const
考慮一個情況,在前面的例子中,我們的對象a是一個非常量對象,如果我們想使用類別A聲明一個常數對象怎麼辦?例如
A
int main() { const int ci = 10; int i = ci; const A ca(5); // ca.x = 5 int j = ca.get(); // 编译错误 }
對於內建類型,可以直接定義常數ci,並且使用它的值。那對於我們自己定義的類別A,我們也可以用相同的方法定義常數ca,透過ca.get()取得物件a的成員x的值。
ci
ca
ca.get()
但是,在使用成員函數取得它的資料成員x的值的時候,我們實際上仍然透過this指標來存取成員x,問題是,此時this指標不是const A *const型,而是A *const類型。我們知道,不可以透過非常量的指標來存取常數的內容,即
const A *const
const int ci = 5; int *p = &ci; // 错误 const int *cp = &ci; // 正确
那麼如何透過this指標來存取成員x呢?我們需要想個辦法讓this指標變成const A *const型。辦法就是,在類別的成員函數宣告末尾加上const關鍵字,這樣就表示該成員函數是 const成員函數,而函數內this指標是常數型別。舉例來說,我們可以重載get()方法:
int A::get() const { return x; }
此時,
int j = ca.get(); // j = 5
就會正常運作了。
具體到你的問題來說,
當我在抽象基底類別的函數宣告後面加上const後,就不會出現型別不相容,這是為什麼?
在store_file()函數內,其參數img_file是一個常數參考類型,想要透過一個常數物件呼叫成員函數,就需要這個物件的成員函數的this指標也是常數型別。
store_file()
img_file
當你在抽象基底類別的get_file_name()函數宣告後面加上const以後,這個函數內的this指標就從Image_file *const型別變成了const Image_file *const型,此時return file_name + std::string(".gif");裡的成員file_name是透過常數類型的this指標呼叫的。從而滿足了上述要求。
get_file_name()
Image_file *const
const Image_file *const
return file_name + std::string(".gif");
file_name
const物件只能呼叫const成員函數
const限定的變數以及其引用只能呼叫const限定的成員函數
const限定代表變數的值不會改變,而要想不改變變數的值則只能呼叫const修飾的成員函數
你的問題其實與多態無關,要解答你這個問題需要兩個方面的知識點。
1. 成員函數的
this
指標首先,類別的成員函數內要存取類別的成員,需要透過
this
指標存取(名稱不衝突時可省略),透過一個物件呼叫成員函數的時候會將物件本身透過this
指標傳遞給成員函數。例如:為什麼我能在
a.get()
函數裡存取物件a
的成員x
?就是透過這個this
指針。在透過a
呼叫a.get()
的時候,編譯器會把物件a
的位址賦值給this
指針,這裡的this
指標的型別是A *const
。而在get()
函數內,return x;
這個語句其實相當於return this->x;
,這樣我們就能夠透過a.get()
來存取物件a
的成員x
了。同理,為什麼
a.set(10);
能夠把物件a
的成員x
賦值成10?也是透過this
指針。x = xx;
就相當於this->x = xx;
。以上兩種情況下,由於
x
的解析不可能出現歧義,所以this
指標可以省略。但是,如果我想在成員函數set
的參數裡使用與成員x
相同的變數名稱該怎麼辦?依然透過this
指針進行區分。我們可以把成員函數set
的定義修改如下:這裡的
this
指標就不能省略了,否則的話,寫成x = x;
就會出現歧義,編譯器無法解析x
到底是函數的參數還是物件的成員。2.
const
成員函數考慮一個情況,在前面的例子中,我們的對象
a
是一個非常量對象,如果我們想使用類別A
聲明一個常數對象怎麼辦?例如對於內建類型,可以直接定義常數
ci
,並且使用它的值。那對於我們自己定義的類別A
,我們也可以用相同的方法定義常數ca
,透過ca.get()
取得物件a
的成員x
的值。但是,在使用成員函數取得它的資料成員
x
的值的時候,我們實際上仍然透過this
指標來存取成員x
,問題是,此時this
指標不是const A *const
型,而是A *const
類型。我們知道,不可以透過非常量的指標來存取常數的內容,即那麼如何透過
this
指標來存取成員x
呢?我們需要想個辦法讓this
指標變成const A *const
型。辦法就是,在類別的成員函數宣告末尾加上const
關鍵字,這樣就表示該成員函數是const
成員函數,而函數內this
指標是常數型別。舉例來說,我們可以重載get()
方法:此時,
就會正常運作了。
應用
具體到你的問題來說,
在
store_file()
函數內,其參數img_file
是一個常數參考類型,想要透過一個常數物件呼叫成員函數,就需要這個物件的成員函數的this
指標也是常數型別。當你在抽象基底類別的
get_file_name()
函數宣告後面加上const
以後,這個函數內的this
指標就從Image_file *const
型別變成了const Image_file *const
型,此時return file_name + std::string(".gif");
裡的成員file_name
是透過常數類型的this
指標呼叫的。從而滿足了上述要求。