生活随笔
收集整理的這篇文章主要介紹了
c++ 继承机制易犯的错误
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
繼承作為面向對象的基本特征之一,其使用率極高。不管是為了實現軟件的基本功能,還是再程序的重構的過程中,我們總是會用到繼承機制。正是因為其用途極為廣泛,而且使用簡單,大眾程序員對其真正的內部實現機制的探究不是很深。而且,在大部分情況下,我們對繼承的使用方法是錯誤的。下面用例子來說明問題。
[cpp]?view plaincopy
class?Animal?{?? ???public:?? ???????Animal?&operator=(const?Animal?&rhs);?? ???????...?? };?? class?Animal1:?public?Animal?{?? ???public:?? ???????Animal1?&operator=(const?Animal1?&rhs);?? ???????...?? };?? class?Animal2:?public?Animal?{?? ???public:?? ???????Animal2?&operator=(const?Animal2?&rhs);?? ???????...?? };??
上面的代碼只是簡單的定義一個繼承體系,即Animal作為基類,Animal1和Animal2公共繼承它。三者都重載了賦值運算符。這對于問題的說明已經足夠了。考慮下面的代碼:
[cpp]?view plaincopy
Animal1?an1;?? Animal2?an2;?? Animal??*pAn1?=?&an1;?? Animal??*pAn2?=?&an2;?? ...?? *pAn1?=?*pAn2;??
上面的代碼足夠簡單了吧!問題就出現了。我們在最后一行的目的是將an2賦值給an1.如果你不是此目的,那么可以繞開本文了!因為通過指針,對對象進行賦值動作對于c++程序員來說,非常普遍。但是實際的效果確是,an1的Animal成分與an2的Animal成分相同,而an1的Animal1成本保持不變。
這里提一下出現這種情況的原因:1.繼承體系中的賦值函數是重載,而不是覆蓋和隱藏(注意三者的區別:);2.由于Animal *pAn1 = &an1,是產生pAn1所覆蓋的范圍縮小的效果,因此當采用賦值操作時,實際上調用的賦值函數時基類的賦值函數
。這種效果是不是導致你的an1對象二不象了,既不是原來的an1對象,也不是你期待的an2對象。不過,如果你是想達到這種移花接木的效果,那么我恭喜你,這種用法太妙了,也說明你對c++ 的繼承體系已經到了一種登峰造極的地步。
? ? ? ? ?不過,大部分人都不是實現移花接木的功能,那么怎么實現全部成分的賦值效果呢?
? ? ? ? ?既然已經用到了繼承機制,那么就不得離開虛函數了。我們將賦值操作符函數定義為虛函數,代碼如下:
[cpp]?view plaincopy
class?Animal?{?? ???public:?? ???????virtual?Animal?&operator=(const?Animal?&rhs);?? ???????...?? };?? class?Animal1:?public?Animal?{?? ???public:?? ???????virtual?Animal1?&operator=(const?Animal1?&rhs);?? ?...};class?Animal2:?public?Animal?{?public:?virtual?Animal2?&operator=(const?Animal2?&rhs);?? ...};??
采用虛函數確實能夠解決上面提到的全部成分的賦值效果,因為他會導致覆蓋賦值函數,而不是上面的重載,因此會調用實際Animal1類的賦值函數。但這樣仍然會帶來問題,如下的代碼:
[cpp]?view plaincopy
Animal1?an1;?? Animal2?an2;?? Animal??*pAn1?=?&an1;?? Animal??*pAn2?=?&an2;?? ...?? *pAn1?=?*pAn2;??
這樣子會允許異型轉換,明顯還是會出現問題。如何解決呢?可以參考《More Effective c++》里面的條款34。
總結
以上是生活随笔為你收集整理的c++ 继承机制易犯的错误的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。