Effective C++物件導向與繼承

來源:互聯網
上載者:User

1:子類不要覆寫父類的非虛函數。

2:子類不要覆寫從父類繼承過來的預設參數

3:子類與父類之間的賦值問題

 

1:子類不要覆寫父類的非虛函數。

為瞭解釋方便,先看一個簡單的例子。

class A{    public:         A(int d):data(d){  }        void print()        {            cout<<"A print..."<<data<<endl;        }        virtual void test(int i=2)        {            cout<<"A test..."<<i<<endl;        }    private:         int data;};class B:public A{    public :                  B(int d):A(d){  }        void print()        {            cout<<"B print..."<<endl;        }         virtual void test(int i=4)        {            cout<<"B test..."<<i<<endl;        }};  //測試代碼int main() {     {        B b(5);         b.print();        A *a=&b;         a->print();          cout<<endl;        b.test();        a->test();        cout<<endl;        A a1=b;        a1.test();    }          getchar();     return 0;}   

運行結果:

例子中指標a是指向對象b的,但是他們調用的print方法卻不是同一個。這裡涉及到靜態繫結和動態綁定的問題。a的靜態類型是A,a的動態類型卻是B,b的靜態類型和動態類型都是B,因為靜態類型就是申明時的類型,動態類型是其真正指向的類型。還有一點就是非虛方法是靜態繫結,虛擬方法是動態綁定。Print是非虛方法,它是靜態繫結,調用的是自己的對象申明類型的方法,所以a調用的是A的print,b調用的是B的print方法。我想我們更想知道C++是怎麼實現動態綁定。我們都知道含有虛方法的類都有一個虛擬方法表,每個對象的執行個體都有一個指標指向這個虛擬方法表,子類會繼承父類的virtual方法,也可以覆寫父類的虛擬方法,如果子類覆寫父類的虛擬方法,那麼在虛擬表中對應的指標就指向子類覆寫父類的方法,如果子類不覆寫父類的虛擬方法,則還是指向父類的方法,這樣就形成了動態綁定。不同的子類按照自己的方式覆寫父類的虛擬方法,表現出不同的行為這就是多態。在多重繼承中,每個對象可能有多個虛擬表,那麼它的執行個體就會有多個指向虛擬表的指標,如果多個父類有一個相同的方法,那麼你就不能直接用這個執行個體調用這個方法,因為編譯器根本不知道它該調用哪個方法,你要指定是那個父類的方法,當你指明了哪個父類,編譯就可以通過對應的指標調用對應的虛擬表中對應的方法。那麼執行個體調用虛擬方法的過程是怎麼樣的呢,你有沒有想過?其實上面也提到一點,大致三步:

1:根據對象的vptr指標找到其虛擬方法表vtbl;

2:找到被呼叫者法在vtbl中對應的指標;

3:調用2中指標指向的方法。

 

2:子類不要覆寫從父類繼承過來的預設參數

這一條其實還是涉及到靜態繫結和動態綁定的問題,關於這個問題我想上面已經說得比較清楚了,預設值也是靜態繫結,這是毫無疑問的,因為它在編譯期就已經確定了,而虛擬方法確實動態綁定,你把靜態繫結的東西和動態綁定的東西攪在一起沒有問題,但是你還有得寸進尺的在子類中覆寫靜態東西就會出問題,對不起,父類不管子類中靜態東西,它只管自己靜態東西,所以當子類不要覆寫從父類繼承過來的預設參數時,子類就可能出現精神分裂的行為,上面那個列子就是證明。

 

上面更多提到的都是關於虛擬方法的,那麼非虛擬方法呢,對象執行個體時怎麼調用非虛擬方法的呢?非虛擬方法是怎麼實現的呢?非虛擬方法就像一般的C函數那樣被實現的,所以他們的調用不需要像虛擬方法一樣先要找到一個指標,然後在通過這個指標調用對應的方法。

 

3:子類與父類之間的賦值問題

首先將父類轉換成子類的事最好不要做,因為子類的很多特性父類根本沒有,當你把一個從父類轉換過來的子類,當做子類來用的話,很可能出問題。接下來我們重點討論將子類轉換成父類。還是通過上面例子來說明問題。

B b(2);

A a=b;//調用copy constructor

a=b;//調用 operator=

上面兩行代碼,第一行先執行個體化了一個對象b,第二行將b賦給a,那麼是怎麼將b賦給a的呢,這裡其實調用的不是operator=,而是copy constructor,因為構造一個對象必須調用constructor,或是copy constructor,那麼這裡肯定是調用copy constructor,operator=只是一個賦值動作,一個對象還沒有構造出來怎麼給他賦值呢,在operator=可不是用來幫你構造對象的哦,在第三行的時候a已經被構造出來了,那麼這裡真的就是賦值了調用的就是operator=。總之一句話,一個對象作為左值時,第一次肯定調用的是copy constructor,被初始化後(分配了記憶體),之後的操作才是賦值。一個對象作為by value形式的參數,那麼每次調用的都是copy constructor,而不是operator=,我們一般都會說將實參賦給形參,其實是用實參構造一個形參。

將b賦給a,就是將b的A部分賦給a,a就是一個完全的A了,它對B一無所知,更不會表現出B的任何行為,所以by value是很暴力並且很耗效能的,也不會出現多態的行為。所以要避免使用by value,盡量用by reference。

就此打住,未完待續...

 

Effective C++系列:

      Effective C++建構函式解構函式Assignment運算子

  Effective C++ 類與函數的設計和申明

    Effective C++物件導向與繼承

作者:陳太漢

部落格:http://www.cnblogs.com/hlxs/

 

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.