天天看點

面向對象程式設計的幾個關鍵概念繼承、多态、組合

一、繼承、接口與多态的相關問題:

1、 繼承的作用?好處?壞處?

繼承:通過繼承實作代碼複用。Java中所有的類都是通過直接或間接地繼程java.lang.Object類得到的。繼承而得到的類稱為子類,被繼承的類稱為父類。子類不能繼承父類中通路權限為private的成員變量和方法。子類可以重寫父類的方法,及命名與父類同名的成員變量。但Java不支援多重繼承,即一個類從多個超類派生的能力。

優點:a因為大部分是繼承而來的,實作代碼重用,減少代碼書寫量;

b很容易修改和擴充已有的實作

缺點:a打破了封裝,因為基類向子類暴露了實作細節

b白盒重用,因為基類的内部細節通常對子類是可見的

c當父類的實作改變時可能要相應的對子類做出改變

d不能在運作時改變由父類繼承來的實作

2、 接口的好處?壞處?

優點:幫助Java語言實作一個類似于多繼承的功能.但是實作的多繼承功能不會使代碼中的類之間出現網狀關系,而是比較清楚的樹狀關系,類似于家譜的感覺。

缺點:如果向一個java接口加入一個新的方法時,所有實作這個接口的類都得編寫具體的實作。




4、 什麼是重載?什麼是重寫?

重載:

a方法重載是讓類以統一的方式處理不同類型資料的一種手段。多個同名函數同時存在,具有不同的參數個數/類型。重載Overloading是一個類中多态性的一種表現。

b Java的方法重載,就是在類中可以建立多個方法,它們具有相同的名字,但具有不同的參數和不同的定義。調用方法時通過傳遞給它們的不同參數個數和參數類型來決定具體使用哪個方法, 這就是多态性。

c重載的時候,方法名要一樣,但是參數類型和個數不一樣,傳回值類型可以相同也可以不相同。無法以傳回型别作為重載函數的區分标準。

重寫:

a父類與子類之間的多态性,對父類的函數進行重新定義。如果在子類中定義某方法與其父類有相同的名稱和參數,我們說該方法被重寫 (Overriding)。在Java中,子類可繼承父類中的方法,而不需要重新編寫相同的方法。但有時子類并不想原封不動地繼承父類的方法,而是想作一定的修改,這就需要采用方法的重寫。方法重寫又稱方法覆寫。

b若子類中的方法與父類中的某一方法具有相同的方法名、傳回類型和參數表,則新方法将覆寫原有的方法。如需父類中原有的方法,可使用super關鍵字,該關鍵字引用了目前類的父類。

c子類函數的通路修飾權限不能少于父類的;

5、 什麼是組合?

組合: a通過建立一個由其他對象組合的對象來獲得新功能的重用方法

b新功能的獲得是通過調用組合對象的功能實作的

c有時又叫聚合

優點:a被包含對象通過包含他們的類來通路

b黑盒重用,因為被包含對象的内部細節是不可見的

c很好的封裝

d每個類專注于一個任務

e通過獲得和被包含對象的類型相同的對象引用,可以在運作時動态定義組合的方式

缺點:a結果系統可能會包含更多的對象

b為了使組合時可以使用不同的對象,必須小心的定義接口

      
類是面象對象程式設計(OOP)的基本概念。OOP将資料和函數封裝到稱為類的玩意中。
繼承是軟體複用的一種形式,實作這種形式的方法是從現有的類建立新類,新類繼承了現有類的方法和屬性,同時新類又可以定義自己的方法和屬性。軟體複用縮自短了開發時間。繼承的魅力在于能夠添加基類沒有的特點進而對基類進行改進。      
例如:在VC開發環境中有類庫(MFC,ATL),他們就是已經編好了的類,在開發軟體時可以對類庫中的類進行擴充、改進,這在實際的開發中經常用到。做法就是以類庫中的類為基類構造自己的類。說白了,繼承是面向對象的基本特征,其最大的用處是可以軟體複用。

多态是比較進階的特性,另一個進階特性是模闆,這兩個在構造複雜系統時才用得上,如MFC類庫中大量運用了多态特性,而ATL将模闆運用得淋漓盡緻。然而我們在學習C++之初對多态、模闆并不重視,而将大量精力放在了指針、繼承、重載等之上。當然這是合理的,因為指針、繼承等是最基本的知識。

在學習C++之初對多态沒有正真了解,直到學了MFC後,真正了解了多态有如些大的用途,C++是如此有魅力,而學了ATL之後才明白原來模闆可以這麼用。

多态分為模闆的靜态多态表現和類繼承的動态綁定兩種.
C++編譯器在編譯的時候,要确定每個對象調用的函數的位址,這稱為早期綁定。與之對應的就是晚綁定。動态多态就是其中一種。
動态多态主要展現在虛函數的調用。在C++中,虛函數的調用使用的是動态綁定,也有人說是晚綁定、運作時綁定,也就是在程式運作時才決定調用的函數。這是通過類的虛表實作的(了解即可)

請注意,C++中多态的實作隻在通過基類指針調用虛函數時才會實作。

關于多态下面舉個例子就明白了:
如我們定義了一個動物類,動物都有一個共同的行為就是呼吸,那麼把呼吸定義在基類中(這很好了解)。
在設計軟體時我們可能會基于程式的靈活性或算法特殊要求而使用基類指針來調用派生類的方法。
比如基于上述類我們開發一個展示動物呼吸的程式,定義了幾百種動物并實作了每種動物的breathe方法,使用者通過滑鼠點選決定看哪一種動物的呼吸方式。

設計時考慮到,使用者并不是每次都想看完這幾百種動物的呼吸,是以我們沒有必要預先産生各種動物的對象,而隻要在使用者點了某種動物後使用new操作符動态建立該對象即可,這樣可以簡化程式設計和節省記憶體空間。
class animal
{
...
       virtual  void breathe() //注意這是慮函數
       {
              cout<<"animal breathe"<<endl;
       }
};

class fish:public animal
{
...
void breathe()
{
     cout<<"fish bubble"<<endl;
}
};

class cat:public animal
{
...
void breathe()
{
     cout<<"cat bubble"<<endl;
}
};


void main()
{
animal* pAn;
switch(使用者選擇)
{
case fish:
pAn = new fish;
break;
case cat:
pAn = new cat;
break;
....
}
pAn->breathe();
delete pAn;
}
}

當使用者選魚時輸出:fish breathe
當使用者選貓時輸出:cat breathe
但是如果你将基類的breathe函數virtual申明除掉,那麼不管你怎麼選都是輸出

animal breathe 
C++中多态的實作隻在通過基類指針調用虛函數時才會實作

你問:“如題,以及什麼時候必須用以上兩種呢”
我認為沒有必須,一個問題可用多種方法實作,在使用多态能更好的設計算法時就使用多态,在需要軟體複用時就用繼承。但要記住一點:C++中使用多态一定要用基類指針,并且一定要在基類中定義虛函數。      

繼續閱讀