繼承: is a 基類A,派生類B,B is a A!
組合: is a part of 聚合(has a),關聯(holds a)
動態特性:
絕大多數情況,程序的功能是在編譯的時候確定下來,此爲靜態特性,若是運行時候確定下來,則是動態特性。
C++虛函數,抽象基類,動態綁定(Dynamic binding),多態(Polymorphism)構成了出色的動態特性..
虛函數:子類自己去實現。一旦一個函數被聲明爲虛函數,派生類自動成爲虛函數,一級級傳遞。建議每一個都加virtual
抽象基類:
Abstract Class抽象類,無法實例化成一個對象。 將基類的虛函數定義爲純虛函數 virtual void f()=0;
抽象基類唯一目的就是讓其派生類繼承並實現它的接口方法。實現“接口與實現分離”。
純虛函數告知編譯器不用爲函數編址,從而阻止該類的實例化。
抽象基類可分爲好幾層的抽象類。如第一層Shape 第二層Shape2D,Shape3D,抽象基類將提供豐富的接口函數供調用,這些都是public的虛函數,這樣的類 叫做接口類。
抽象基類不能實例化,並且實現類被完全隱藏,所以必須以其他的途徑獲得實現類的對象,比如提供入口函數來動態創建實現類的對象,入口函數可以是全局函數,也可以使靜態成員函數。
PubilcBase* create(){return new PublicBase; }
#include <iostream>
using namespace std;
class Base{
public:
virtual void f()=0;//純虛函數,Base爲抽象基類
virtual void g()=0;//純虛函數
static Base* create();//抽象基類無法實例化,
//創建靜態成員函數來動態創建類的對象。
//入口函數
};
class B:public Base
{
public:
virtual void f(){}//自動爲虛函數,加上virtual更好
virtual void g(){}
};
Base* Base::create(){return new B;}//注意靜態成員函數寫在外面!
int main()
{
Base* b = Base::create();
b->f();
b->g();
return 0;
}
動態綁定:父類指針指向子類對象,可以調用虛函數。
每一個具有虛函數的多態類的對象內存有4個字節的大小存放虛表指針,指向虛函數表,虛函數表本質是一個函數指針數組,存放着這個類所有的虛函數的地址,包括那些繼承的但未改寫的虛函數。
#include <iostream>
using namespace std;
class A{
int a;
public:
virtual void f(){}
};
class B:public A{
public:
virtual void f(){}
virtual void f(int n){}
};
int main()
{
A* p = new B;
p->f();//正確
p->f(1);//編譯錯誤,A類無此類原型函數,除非p類型爲B*
//派生定義中的名字(對象或者函數名)將會義無反顧的遮蔽掉
//基類中任何的同名的對象或者函數。
return 0;
}
派生類定義了一個與其基類的虛函數同名的函數,但是參數表不同,編譯器不會認爲這個是對虛函數的改寫,而是隱藏。所以不會發生運行時綁定,相反,要想達成運行時綁定的效果,同名虛函數必須相同的原型,即參數表相同(返回類型可以不同,C++特徵===協變)
虛函數面臨的難題:抽象基類無法實例化,基類指針指向派生類對象,並不能告訴我們到底是哪一個子類對象,因而不能進行相應的處理,僅有的靜態類型檢查和虛函數機制不足以解決所有的問題。所以我們有了RTTI(Run-time Type Identification)
RTTI:
typeid:typeid()運算符以一個對象或者類型名作爲參數,返回一個匹配的const type_info對象,它表明該對象的確切類型。
常用的三個成員函數; operaotr==(),operator()!=(),name()
if(typeid(Base* p)==typeid(child)) ====>通過基類指針判斷類型
dynamic_cast<>運算符:派生類對象應該也是基類的對象,typeid()不存在這種功能
dynamic_cast<>可以轉換指針和引用,不能轉換對象。
基類指針或引用===>子類指針或引用(downcast) 子類指針或引用====>基類指針或引用(upcast)
dynamic_cast<目標類型>(被轉換的類型) 目標類型爲某種類型的指針(包括void*),成功轉換則返回目標類型 的指針,否則返回NULL
目標類型爲某種類型的引用,成功則返回目標類型引用,失敗返回std::bad_cast,不存在NULL引用。
注意:
1.dynamic_cast<>轉換引用,需要catch std::bad_cast
2.試圖用typeid來檢索Null所指對象的類型,typeid(*p) 將拋出bad_typeid異常
3.dynamic_cast<>轉換指針,記住返回值是否爲NULL。