[英]Tricky Polymorphism and virtual functions
我有以下代碼。
#include <iostream>
using namespace std;
class K {
public:
virtual void add_st(K* n) {
cout << "add_st (K*) from K\n";
}
};
class L: public K {
public:
virtual void add_st(L* a) {
cout << "add_st (L*) from L\n";
}
};
int main() {
L ob, ob2;
K k, *pl = &ob;
pl->add_st(&ob2);
return 0;
}
該程序的輸出為:
add_st (K*) from K
如果我什么都沒錯過的原因是虛函數表。 從層次結構的頂部一直到最低的類生成對象。
但是這段代碼:
#include <iostream>
using namespace std;
class K {
public:
virtual void add_st() {
cout << "add_st (K*) from K\n";
}
};
class L: public K {
public:
virtual void add_st() {
cout << "add_st (L*) from L\n";
}
};
int main() {
L ob, ob2;
K k, *pl = &ob;
pl->add_st();
return 0;
}
將打印
add_st (L*) from L
為什么?
虛函數在參數列表上是不變的,在返回類型上是協變的。
想到這一點的基本方法是,在基類中引入了虛擬成員函數的地方,它定義了一個契約。
例如,給定
struct K
{
virtual K* add_st(K* n);
};
契約是add_st
接受任何類型為K
對象 (通過指針),並返回類型為K
的對象 (按指針)。
這將覆蓋它
struct L : K
{
virtual K* add_st(K* a);
};
因為已清楚地履行了合同,所以:
struct M : K
{
virtual M* add_st(K* a);
};
因為return是M
類型的對象,通過繼承它也是K
類型的對象; 合同已履行。
但這(問題中的情況)不會被覆蓋
struct N : K
{
virtual K* add_st(N* a);
};
因為它不能接受任何類型為K
對象,所以只能接受類型均為K
和N
。 這也不是:
struct P : K
{
virtual K* add_st(void* a);
};
即使從類型理論的角度來看,逆向參數是兼容的,事實是C ++支持多重繼承,並且上載有時需要指針調整,因此逆向參數類型在實現級別中斷。
他們將創建一個新函數(v表中的新插槽),該函數將重載並隱藏現有函數,而不是覆蓋現有函數。 (正如約翰·史密斯(John Smith)在回答中所說,可以使用using聲明來避免隱藏基本版本)
以下是錯誤,因為簽名相同,但是返回類型不兼容:
struct Q : K
{
virtual void* add_st(K* a);
};
這里的結果可以是任何對象類型,但這還不夠好,合約需要類型為K
的對象。 而且它不能覆蓋現有函數,因為參數沒有區別。 因此它只是被拒絕。
有關方差的更多詳細信息,您可能需要閱讀Liskov替代原理 。
首先,功能簽名包括功能名稱及其參數類型。 在第一個示例中,函數名稱相同,但其參數類型不同。 因此,它們具有不同的簽名。 因此,在第一個示例中,子類中的函數未覆蓋其父類中的函數。
其次還有overload
和名稱隱藏的概念。 在您的情況下,第一個示例中的函數定義將隱藏其父函數。 如果將父函數帶入相同的作用域,則子函數將重載父函數,如下所示
class L: public K {
public:
using K::add_st;
virtual void add_st() {
cout << "add_st (L*) from L\n";
};
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.