第十二章 C++ 面向對象設計方法概述

繼承: 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。

                                                  

                               

發佈了37 篇原創文章 · 獲贊 2 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章