本節主要須要區分的是:
1.每乙個派生類物件包括乙個基類部分。因此。能夠像使用基類物件一樣在派生類物件上執行操作。
基於這一點,能夠將派生類物件的引用(指標)轉換為基類子物件的引用(指標),且存在自己主動轉換。
反之,基類到派生類的自己主動轉換是不存在的,因此基類不包括派生型別的成員。另外,將基類指標或引用繫結到派生類物件時也存在限制,由於編譯器編譯時無法知道該轉換是安全的(編譯器確定轉換是否合法,僅僅看指標或引用的靜態型別)。假設我們確定基類到派生類的轉換是安全的,能夠使用static_cast強制編譯器進行轉換,也能夠用dynamic_cast申請在執行時進行檢查。
2.引用轉換不同於物件轉換。
在引用轉換中。物件本身未被複製,轉換不會在不論什麼方面改變派生型別物件。
在物件轉換中(不是引用或指標),形參型別是固定的。將派生類物件傳給基類物件形參時,該派生類物件的基類部分被【複製】到形參,這裡是對基類物件進行初始化或賦值:初始化時呼叫建構函式,賦值時呼叫賦值操作符。(其實,一般基類的複製建構函式和賦值操作符的形參是基類型別的const引用,由於存在從派生類引用到基類引用的轉換,故這兩個成員函式可用於從派生類物件到基類物件進行初始化或賦值)。
樣例1:
class bulk_item: public item_base
};
該建構函式隱式呼叫基類的預設建構函式初始化物件的基類部分:首先使用item_base的預設建構函式初始化item_base部分,之後再初始化bulk_item部分的成員,並執行建構函式的函式體。
樣例2:
class bulk_item: public item_base
};
該建構函式使用有兩個形參的item_base建構函式初始化基類子物件。
*首先初始化基類。然後依據【宣告次序】初始化派生類的成員。
*乙個類僅僅能初始化自己的【直接】基類。
*僅僅包括類型別或內建型別資料成員,不含指標的類一般能夠使用合成操作。複製、複製或撤銷這種成員不須要特殊控制。
假設派生類定義了自己的複製建構函式,該複製建構函式一般應【顯式】使用基類複製建構函式初始化物件的基類部分:
class base ;
class derived: public base
};
base(d)將派生類物件d轉換為它的基類部分的引用,並呼叫基類複製建構函式。
*假設忽略base(d)。則將執行base的【預設】建構函式初始化物件的基類部分——並不符合我們【複製】的本意。
*對於複製操作符。必須防止自身賦值。
derived &derived::oprator=(const derived &rhs)
return *this;
}
派生類析構函式不負責撤銷基類物件的成員。【編譯器】總是【顯式】呼叫派生類物件基類部分的析構函式。每乙個析構函式僅僅負責清楚自己的成員:
class derived: public base
};
*物件的撤銷順序與構造順序相反:先執行派生類析構函式。然後按繼承層次依次向上呼叫個基類析構函式。
要保證執行適當的析構函式。基類中的析構函式必須為虛函式:
class
item_base
};item_base *itemp = new item_base;
delete itemp; //呼叫item_base的析構函式
itemp = new bulk_item; //指標的靜態型別和動態型別不同
delete itemp; //呼叫bulk_item的析構函式
在上述情況中。假設我們不定義虛析構函式。則第二個delete呼叫的將是item_base的析構函式,這對於派生型別是沒有定義的行為。
*建構函式不能定義為虛函式。賦值操作符設為虛函式則可能會令人混淆,並且不會有什麼用處。
首先須要明白:
構造派生類物件時先執行基類建構函式,此時物件的派生類部分是未初始化的。
撤銷派生類物件時先撤銷派生類部分。
在這兩種情況下,物件都是不完整的。
因此,在基類建構函式或析構函式中,編譯器將派生類物件【以基類型別物件對待】。
假設在建構函式或析構函式中呼叫虛函式。執行的是建構函式或析構函式自身型別定義的版本號。
理由:假設從基類建構函式中呼叫虛函式的派生類版本號,則派生類版本號可能會呼叫派生類成員。而此時物件的派生部分成員還未初始化!
該訪問將可能導致程式崩潰。
在繼承情況下,派生類的作用域巢狀在基類作用域中。
假設不能在派生類作用域中確定名字,就在外圍基類作用域中查詢該名字的定義。
與基類成員同名的派生類成員將遮蔽對基類成員的直接訪問(可通過作用域操作符訪問被遮蔽的基類成員)。
注意。對於函式。即使函式原型不同。僅僅要名字相同。基類成員就會被遮蔽。
區域性作用域中宣告的函式不會過載全域性作用域中定義的函式,相同,派生類中定義的函式也不過載基類中定義的成員。
因此,假設基類有下面函式:
int memfcn();
而派生類有下面函式:
int memfcn(int);
則對派生類呼叫memfcn()將報錯。因此不存在對基類函式的過載,基類中的memfcn()被遮蔽。
這也解釋了虛函式為什麼必須在基類和派生類中擁有同一原型。
看下面**:
class base
;class d1: public base
;class d2: public d1
;base bobj;
d1 d1obj;
d2 d2obj;
base *bp1 = &bobj, *bp2 = &d1obj, *bp3 = &d2obj;
bp1->fcn(); //call base::fcn at run time
bp2->fcn(); //call base::fcn at run time
bp3->fcn(); //call d2::fcn at run time
三個指標都是基類型別的指標,故通過在base中查詢fcn確定這三個呼叫。
由於fcn是虛函式,所以編譯器會生成**,在執行時基於引用或指標所繫結的實際型別進行呼叫。
在函式形參表後面寫上=0指定純虛函式:
double fcn(std::size_t) const = 0;
含有乙個或多個純虛函式的類是抽象基類,除了作為抽象基類的派生類的物件的組成部分。不能建立抽象型別的物件。
假設乙個容器存放的是基類型別的物件,當插入乙個派生類物件時,會將派生類物件的基類部分【複製】到基類物件並儲存在容器中(因此派生類部分將被切掉。也就是說容器裡的僅僅能是基類物件,而不是派生類物件)。
乙個解決方式是,使用容器儲存物件的指標。
OOP物件導向程式設計 C
oop程式設計的一些概念 一 物件 object 這個概念可以說是物件導向裡面的最為核心的概念,如果找不著物件,又如何物件導向呢?物件,也就是你要處理的問題裡面設計的若干個因素,比如你做學生成績統計,那麼學生當然是你要考慮的物件。二 類 class 從本質上講是先有物件才有類,因為在處理的實際程式設...
物件導向程式設計(OOP)
物件導向程式設計 object oriented programming 作為一種新方法,其本質是以建立模型體現出來的抽象思維過程和物件導向的方法。模型是用來反映現實世界中事物特徵的。任何乙個模型都不可能反映客觀事物的一切具體特徵,只能對 事物特徵和變化規律的一種抽象,且在它所涉及的範圍內更普遍 更...
OOP物件導向程式設計
oop 達到了軟體工程的三個主要目標 重用性 靈活性和擴充套件性。元件 資料和功能一起在執行著的電腦程式中形成的單元,元件在 oop 電腦程式中是模組和結構化的基礎。抽象性 程式有能力忽略正在處理中資訊的某些方面,即對資訊主要方面關注的能力。封裝 也叫做資訊封裝 確保元件不會以不可預期的方式改變其它...