C++重寫重載_詳細說明C++中的函數名字重寫、重載、重定義程序應用案例
發表時間:2023-07-27 來源:明輝站整理相關軟件相關文章人氣:
[摘要]C++重寫重載_詳解C++中的函數名字重寫、重載、重定義程序應用實例重載、重寫是必須要知道,因為用途太廣泛;至于隱藏嗎,完全是C++為面試官設計的(^_^等待挨磚)。有些面試主考官總喜歡拿這三個概念...
C++重寫重載_詳解C++中的函數名字重寫、重載、重定義程序應用實例
重載、重寫是必須要知道,因為用途太廣泛;至于隱藏嗎,完全是C++為面試官設計的(^_^等待挨磚)。有些面試主考官總喜歡拿這三個概念去為難你,考察你的C++基礎是否牢固。所以為了面試、這三個概念還是需要我們去區分一下。
JAVA中語言中方法(函數)調用有兩種特殊的形態:重載與重寫;而C++由于增加了virtual這個虛函數關鍵字,給函數調用又增加了變數:除了重載、重寫(也稱覆蓋)之外還多了隱藏這么一說。
C++中經常出現函數名字一樣,但參數列表或返回值不同的函數,要搞清楚函數的正確調用關系,需理清三個概念:重寫(override)、重載(overload)、重定義(redefine)。
1、重載的特征:在同一個類中;函數名字相同;參數不同;virtual 關鍵字可有可無。
2、重寫(覆蓋)特征是:分別位于派生類與基類;函數名字相同;參數相同;基類函數必須有virtual 關鍵字(這點非常要注意)。
C++的隱藏規則使問題復雜性陡然增加。規則如下:
1、如果派生類的函數與基類的函數同名,但是參數不同。此時,不論有無virtual關鍵字,基類的函數將被隱藏(注意別與重載混淆)。
2、 如果派生類的函數與基類的函數同名,并且參數也相同,但是基類函數沒有virtual關鍵字。此時,基類的函數被隱藏(注意別與重寫混淆)。
一、三個基本概念
1、重定義(redefine):派生類對基類的成員函數重新定義,即派生類定義了某個函數,該函數的名字與基類中的函數名字一樣。
特點:(1)不在同一個作用域(分別位于基類、派生類) (2)函數的名字必須相同 (3)對函數的返回值、形參列表無要求
特殊情況:若派生類定義的該函數與基類的成員函數完全一樣(返回值、形參列表均相同),且基類的該函數為virtual,則屬于派生類重寫基類的虛函數。
作用效果:若重新定義了基類中的一個重載函數,則在派生類中,基類中該名字的函數(即其他所有重載版本)都被自動隱藏,包括同名的虛函數。
2、重載(overload):函數名字相同,但它的形參個數或者順序,或者類型不同,但是不能靠返回類型來判斷。
特點:(1)位于同一個類中 (2)函數的名字必須相同 (3)形參列表不同(可能是參數個數 or 類型 or 順序 不同),返回值無要求
特殊情況:若某一個重載版本的函數前面有virtual修飾,則表示它是虛函數。但它也是屬于重載的一個版本
不同的構造函數(無參構造、有參構造、拷貝構造)是重載的應用
作用效果和原理:編譯器根據函數不同的參數表,將函數體與函數調用進行早綁定。重載與多態無關,只是一種語言特性,與面向對象無關。
3、重寫(override):派生類重定義基類的虛函數,即會覆蓋基類的虛函數 (多態性)
特點:(1)不在同一個作用域(分別位于基類、派生類) (2)函數名、形參列表、返回值相同 (3)基類的函數是virtual
特殊情況:若派生類重寫的虛函數屬于一個重載版本,則該重寫的函數會隱藏基類中與虛函數同名的其他函數。
作用效果:父類的指針或引用根據傳遞給它的子類地址或引用,動態地調用屬于子類的該函數。這個晚綁定過程只對virtual函數起作用
具體原理是由虛函數表(VTABLE)決定的,在第三節介紹。
[page]
二、程序實例
1、兩個類:基類( 取名Test)和派生類( 取名XX) 名字不規范,哈哈隨便取得!
基類和派生類的結構
//Base class
class Test
{
public:
int a;
Test()
{
cout<<"Test() 無參構造函數!"<<>
}
Test(int data)
{
a = data;
cout<<"Test(int data) 有參構造函數!"<<>
}
Test(const Test &tmp)
{
a = tmp.a;
cout<<"Test 拷貝構造函數!!"< }
//基類中對函數名f,進行了重載。其中最后一個重載函數為虛函數
void f()const
{
cout<<"調用 void Test::f()"<<>
}
//overload
int f(int data) const
{
cout<<"調用 Test f(int data)"<<>
return 1;
}
//overload 虛函數
virtual double f(int dataA,int dataB)
{
cout<<"調用 Test f(int a,int b)"<<>
return dataA*dataB/2.0;
}
};
class XX: public Test
{
public:
Test atest;//先調用基類的構造函數,然后對象成員的構造函數,最后才是派生類的構造函數
XX()
{
cout<<"XX() 無參構造函數被調用!"<<>
}
//對基類的函數名f,進行了重定義。則會隱藏基類中的其他f函數
//redefine
int f() const
{
cout<<" 調用 XX f()函數"<<>
return 1;
}
//重寫基類的虛函數
//redefine override
double f(int dataA,int dataB)
{
cout<<"調用 XX f(int dataA,int dataB)函數"<<>
return (dataA+dataB)/2.0;
}
};
分析:基類class Test中定義了名為f的3個重載函數,其中最后一個是虛函數
派生類class XX中對f進行了重定義,所以會隱藏基類中名為f的版本。其中派生類的double f(int dataA,int dataB)屬于對虛函數的重寫
測試---主程序
int main()
{
//-----test 1------------------------
cout<<"-------test 1------------"<<>
//Base class
Test aaTest;
aaTest.f();
aaTest.f(12);
aaTest.f(10,20);
//derived class
XX d;
d.f();
// d.f(2); //error C2661: 'f' : no overloaded function takes 1 parameters
d.f(10,20);
//--------test 2----------------------------------
cout<<"-------test 2------------"<<>
Test b = d;
b.f();
b.f(10,20);//調用的是基類的函數,不發生多態
//--------test 3----------------------------------------
cout<<"-------test 3------------"<<>
Test &bR = d;//引用
b.f();//f()不是虛函數,調用基類的函數
bR.f(10,20);//調用的是派生類的函數,發生多態
//--------test 4--------------------------------------
cout<<"-------test 4------------"<<>
Test* pB = &d;
b.f();
pB->f(10,20);//調用的是派生類的函數,發生多態
return 1;
}
分析:(1)test 1中進行了重載測試,根據傳遞參數的不一樣,調用不同的函數 (早綁定,與多態無關)
(2)test 2中Test b = d;定義了一個基類對象,用派生類對象來進行初始化。這會調用基類的拷貝構造函數,生成基類的對象b,基類的拷貝構造函數初始化b的VPTR,指向b的VTABLE。因此所有的函數調用都只發生在基類,不會產生多態。
這是一個對象切片過程(參見《C++編程思想.第二版》P370),對象切片是當它拷貝到一個新的對象時,會去掉原來對象的一部分,而不是像使用指針或引用那樣簡單地改變地址的內容。
(3)test 3和test 4中,定義的基類指針和引用,故會發生多態。
三、晚綁定原理:虛函數表
當通過基類指針做虛函數調用時(即多態調用時),編譯器靜態地插入能取得這個VPTR并在VTABLE表中查找函數地址的代碼,這樣就能調用正確的函數并引起晚綁定的發生。編譯器會對每一個包含虛函數的類(或者從包含虛函數的基類派生的類)創建一個表(VTABLE),里面存放特定類的虛函數的地址。然后編譯器秘密地放置一指針vpointer(VPTR),指向這個對象的vtable。
學習教程快速掌握從入門到精通的電腦知識