c++三大特性:封裝、繼承、多型。封裝使**模組化,繼承擴充套件已存在的**,多型的目的是為了介面重用
虛函式實現:虛函式表;指標放到虛函式表
多型:同名函式對應到不同的實現
構造父類指標指向子類的物件
father *p = new son();
多型性是允許你將父物件設定成為和乙個或更多的他的子物件相等的技術,賦值之後,父物件就可以根據當前賦值給它的子物件的特性以不同的方式運作。簡單的說:允許將子類型別的指標賦值給父類型別的指標(乙個介面,多種方法)
虛函式的目的就是通知系統在函式呼叫時能夠自動識別對應的類物件型別,從而能夠根據指標所指型別呼叫對應的類物件,實現函式呼叫時的多型性。
c++ 支援兩種多型性:編譯時多型性,執行時多型性
a、編譯時多型性(靜態多型):通過過載函式實現
b、執行時多型性(動態多型):通過虛函式實現
多型的作用:
那麼多型的作用是什麼呢,封裝可以使得**模組化,繼承可以擴充套件已存在的**,他們的目的都是為了**重用。而多型的目的則是為了介面重用。也就是說,不論傳遞過來的究竟是那個類的物件,函式都能夠通過同乙個介面呼叫到適應各自物件的實現方法
c++中共有三種實現多型的方式:第一種是函式過載;第二種是模板函式;第三種是虛函式
1.虛函式:
有virtual才可能發生動態多型現象,無virtual呼叫就按原型別呼叫
虛函式: 就是允許被其子類重新定義的成員函式,子類重新定義父類虛函式的做法,可實現成員函式的動態覆蓋(override)。
純虛函式: 是在基類中宣告的虛函式,它在基類中沒有定義,但要求任何派生類都要定義自己的實現方法。在基類中實現純虛函式的方法是在函式原型後加「=0」
virtualvoid funtion()=0
抽象類:包含純虛函式的類稱為抽象類。由於抽象類包含了沒有定義的純虛函式,所以不能進行例項化。
純虛函式的作用:
a.為了方便使用多型特性,我們常常需要在基類中定義虛函式。
b.在很多情況下,基類本身生成物件是不合情理的。例如,動物作為乙個基類可以派生出老虎、孔雀等子類,但動物本身生成物件明顯不合常理。為了解決上述問題,引入了純虛函式的概念,將函式定義為純虛函式(方法:virtual returntype function()= 0;
),則編譯器要求在派生類中必須予以重寫以實現多型性。同時含有純虛函式的類稱為抽象類,它不能生成物件。這樣就很好地解決了上述兩個問題。
析構函式能否為虛?
能。為什麼基類的析構函式必須為虛?
如果不為虛函式,只釋放基類,不釋放子類,造成記憶體洩漏
基類指標指向子類物件建立例項,而將這個物件釋放的時候,呼叫的是基類析構函式;如果基類析構函式不是虛函式,那麼只會釋放基類的部分,而子類部分不會被釋放。基類析構函式是虛函式時,則呼叫子類析構函式,基類子類都釋放,這樣釋放才完整。
2.函式過載:
函式過載是這樣一種機制:允許有不同引數的函式有相同的名字
具體一點講就是:假如有如下三個函式:
void test(int arg){} //函式1void test(char arg){} //
函式2void test(int arg1,int arg2){} //
函式3
如果在c中編譯,將會得到乙個名字衝突的錯誤而不能編譯通過。在c++中這樣做是合法的。可是當我們呼叫test的時候到底是會呼叫上面三個函式中的哪乙個呢?這要依據你在呼叫時給的出的引數來決定。如下:
test(5); //呼叫函式1
test('
c');//
呼叫函式2
test(4,5); //
呼叫函式3
c++是如何做到這一點的呢?原來聰明的c++編譯器在編譯的時候悄悄的在我們的函式名上根據函式的引數的不同做了一些不同的記號。具體說如下:
void test(int arg) //被標記為 『test有乙個int型引數』
void test(char arg) //
被標記為 『test有乙個char型的引數』
void test(int arg1,int arg2) //
被標記為 『test第乙個引數是int型,第二個引數為int型』
這樣一來當我們進行對test的呼叫時,c++就可以根據呼叫時的引數來確定到底該用哪乙個test函式了。噢,聰明的c++編譯器。其實c++做標記做的比我上面所做的更聰明。我上面哪樣的標記太長了。c++編譯器用的標記要比我的短小的多。看看這個真正的c++的對這三個函式的標記:
test@@yaxd@ztest@@yaxh@z
test@@yaxhh@z
是不是短多了。但卻不好看明白了。好在這是給計算機看的,人看不大明白是可以理解的。 還記得cout吧。我們用《可以讓它把任意型別的資料輸出。比如可以象下面那樣:
cout << 1; //輸出int型
cout << 8.9; //
輸出double型
cout << '
a'; //
輸出char型
cout << "
abc";//
輸出char陣列型
cout << endl; //
輸出乙個函式
cout之所以能夠用乙個函式名<<(《是乙個函式名)就能做到這些全是函式過載的功能。要是沒有函式過載,我們也許會這樣使用cout,如下:
cout int<< 1; //
輸出int型
cout double
<< 8.9; //
輸出double型
cout char
<< '
a'; //
輸出char型
cout chararray<< "
abc"; //
輸出char陣列型
cout function(…)<< endl; //
輸出函式
為每一種要輸出的型別起乙個函式名,這豈不是很麻煩呀。 不過函式過載有乙個美中不足之處就是不能為返回值不同的函式進行過載。那是因為人們常常不為函式呼叫指出返回值。並不是技術上不能通過返回值來進行過載。
3.模版函式:
概念:函式的內容有了,但函式的引數型別卻是待定的(注意:引數個數不是待定的)
比如說乙個(準確的說是一類或一群)函式帶有兩個引數,它的功能是返回其中的大值。這樣的函式用模板函式來實現是適合不過的了。如下:
template < typename t>t getmax(t arg1, t arg2)
這就是基於模板的多型嗎?不是。因為現在我們不論是呼叫getmax(1, 2)還是呼叫getmax(3.0, 5.0)都是走的上面的函式定義。它沒有根據呼叫時的上下文不同而執行不同的實現。所以這充其量也就是用了乙個模板函式,和多型不沾邊。怎樣才能和多型沾上邊呢?用模板特化呀!象這樣:
template<>char* getmax(char* arg1, char*arg2)
這樣一來當我們呼叫getmax(「abc」, 「efg」)的時候,就會執行**段2,而不是**段1。這樣就是多型了。 更有意思的是如果我們再寫這樣乙個函式:
char getmax(char arg1, chararg2)
當我們呼叫getmax(『a』, 『b』)的時候,執行的會是**段3,而不是**段1或**段2。c++允許對模板函式進行函式過載,就象這個模板函式是乙個普通的函式一樣。於是我們馬上能想到寫下面這樣乙個函式來做三個數中取大值的處理:
int getmax( int arg1, int arg2, intarg3)
同樣我們還可以這樣寫:
template t getmax(t arg1, t arg2, t arg3)
現在看到結合了模板的多型的威力了吧。比只用函式過載厲害多了
模版函式就是函式模版生成的具體的函式,比如 getmax(int arg1,int arg2,int arg3)
c 多型,虛函式 過載函式 模版函式
c 三大特性 封裝 繼承 多型。封裝使 模組化,繼承擴充套件已存在的 多型的目的是為了介面重用 虛函式實現 虛函式表 指標放到虛函式表 多型 同名函式對應到不同的實現 構造父類指標指向子類的物件 father p new son 多型性是允許你將父物件設定成為和乙個或更多的他的子物件相等的技術,賦值...
當函式模版遇上函式過載
demo 1 include using namespace std 讓 型別引數化 方便程式設計師進行編碼 泛型程式設計 template 告訴c 編譯器 我要開始泛型程式設計了 看到t,不要隨便報錯 template void myswap t a,t b void myswap int a,c...
當函式模版遇上函式過載
demo 1 include using namespace std 讓 型別引數化 方便程式猿進行編碼 泛型程式設計 template 告訴c 編譯器 我要開始泛型程式設計了 看到t,不要隨便報錯 template void myswap t a,t b void myswap int a,cha...