日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

纯虚函数能为private吗?

發布時間:2024/4/11 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 纯虚函数能为private吗? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

我們把一個僅僅含有純虛函數的類稱為接口,我們也好像已經習慣了將這個接口中的所有純虛函數全聲明為public,而且按照這樣的設計,一切都工作得不錯。比如COM正是這樣做的,它的接口中幾乎不會存在private的純虛函數。那么,讓我們想一想,純虛函數或者虛函數可以為private嗎?如果這種方式是可行的,那么什么時候可以將(純)虛函數設為private了?這些都是本文將要討論的主題。一起來看看。


一.訪問限定符與繼承???

如果基類隱式(間接)向子類暴露了私有成員,那么從某種意義上講,該私有成員對于子類是可見的。

任何一本講C++基礎的課本上都詳細地介紹了訪問限定符與繼承的關系,在這里就不重復了,但是,課本上的東西并不全,不信?那么請先看看下面的例子:

#include?<string>??
#include?
<iostream>
?
using?namespace
?std?;?

class
?Base?
{?
private
:?
????
string?classID()?const

????{?
???????
return?string("Base")?;?
????}?

protected
:?
????
virtual?void?doWork()?=0?;?//純虛函數


public:??
????
void
?work()?
????{?
????????cout
<<"this?class?id?is?"<<classID()<<
endl?;??
????????doWork()?;?
????}?

????
virtual?~
Base()?
????{?
????}?
};?

class?DerivedA?:?public
?Base?
{?
private
:?
????
string?classID()?const
??
????{?
???????
return?string("DerivedA"
)?;
????}?

protected
:?
????
void
?doWork()?
????{?

???????cout
<<"this?is?DerivedA?doWork?!"<<
endl?;?
????}?
};

??? 以上的代碼聲明了一個基類和一個子類,不過比較奇特的是基類的提供的公共接口是非虛的,而這個非虛的公共接口卻調用了一個非虛的私有函數和一個虛擬的保護函數。接著,子類重定義了這兩個函數。那么下面的調用會輸出什么了????

Base*?bp?=?new?DerivedA()?;?
bp
->work()?;??
delete?bp?;?

???以下是輸出的結果:
???this class id is Base?
???this is DerivedA doWork !?

???怎么回事?為什么不是
?
??????this class id is?DerivedA?
??????this is DerivedA doWork !

子類的classID()不是將基類的classID()覆蓋了么?我們來分析一下,基類中的公共的work()成員函數調用了私有的classID()成員函數,根據輸出的結果來看,在子類中定義的classID方法并沒有覆蓋基類的同名方法,為什么呢?難道是因為classIDprivate導致的?那好,我們將classID函數改為public再次運行,我們期望的結果出現了嗎?呵呵,很抱歉,沒有,希望再次破滅了,為什么會這樣?這主要涉及的原因是:普通函數的調用是在編譯期確定的,當work函數一看到所調用的classID是非虛的,就會毫無疑問地去直接使用基類的classID。這一切與Base類是否會被繼承沒有任何關系,跟Base類被繼承后子類會否再次定義classID就更沒有關系了。

???那么這種情況下,
Base類將classID聲明為privatepublic/protected有什么區別了?當將classID聲明為private時,DerivedA看不到基類的classID的聲明,所以不會發生重定義;當將classID聲明為public/protected時,DerivedA將看到基類的classID聲明,于是會發生重定義,即會覆蓋調基類的classID的定義。講到這里就要提一下,如果當將classID聲明為public/protected,并且子類也定義同名的函數classID,但是子類的classID與基類的classID的函數簽名不同,那么此時發生的將是函數重載而不是覆蓋。

讓我們更進一步,將基類和子類的classID聲明都改為virtual public?,再次運行程序,會得到以下輸出: ?
?????????this class id is?DerivedA?
?????????this is DerivedA doWork !

而這正是我們所期望的,不是嗎?這其中的原因也很容易理解,因為classIDvirtual?,并且是public的,所以會產生多態調用。
???
再往下走,將基類和子類的classID聲明改為virtual private?,再次運行程序,看看輸出了什么。
??????this class id is?DerivedA?
??????this is DerivedA doWork !

沒有變化,將classID聲明為virtual private和聲明為?virtual public?得到的結果是一樣的?!盀槭裁磿@樣,classIDprivate啊?”你驚訝地叫出來。是,classIDprivate,但classID也是virtual,原因就在這里,用基類指針或引用進行虛函數調用采用的是動態綁定,看看編譯器為調用classID產生的代碼就知道了:

//c++偽碼

(this->vptr[1])() ;

在運行時期,通過this指針將會找到正確的vtbl,即DerivedA類的vtbl,這樣自然就會出現上面的結果了。那么將classID?聲明為private限制了什么?和將非虛函數聲明為private一樣,這將使得在Base類外部無法調用多態函數classID,只能在Base內部調用,如通過work函數調用。

???可見,多態性與將實現多態的函數的訪問限定符沒有任何關系private?函數仍然可以實現多態,它的指針仍然位于vtbl中,只不過該函數的多態一般只能在基類的內部由其他非虛函數調用該函數的時候反映出來,訪問限定符僅僅限制外部對類的成員的訪問權限,它并沒有破壞以下規則:?

???????
通過基類指針或引用調用成員函數時,如果該函數時非虛的,那么將采用靜態綁定,即編譯時綁定;如果該函數是虛擬的,則采用動態綁定,即運行時綁定。

二.virtual 與訪問限定符結合

上面我們通過分析,已經知道了多態的實現與訪問限定符沒有任何關系,訪問限定符只是控制類的成員對外部的可見性,但不限制多態。正如上面提到的,將classID聲明為virtual private和聲明為?virtual public?后再次運行程序,得到的結果是一樣的,上面我們簡單的地分析了一下表面現象,但這個問題決不是這么簡單,讓我們挖掘更深層次的意義,我想這應該屬于OOA、OOD的范疇了。好,讓我們一步步看過來。

當我們將classID聲明為非虛的?private時,子類將看不見它,當然也就無法覆蓋或重載它,即在這中情況下,子類無法更改classID的實現,但是子類繼承了公共接口work(),而這個接口調用了classID,所以,可以看作,子類間接地繼承了classID的實現,并且這個實現是無法修改的。于是,我可以說,基類中聲明一個普通私有成員函數,表示這是一個不可被更改的實現細節。

再來討論將classID聲明為virtual private的情況,聲明為private表示基類不想讓子類看到這個函數,但是又聲明為virtual,表示基類想讓這個函數實現多態。呵呵,基類既想實現多態,卻又不讓子類看見這個函數,這似乎有點自相矛盾,是嗎?其實,這其中的意思是,子類既可以修改這個實現也可以繼承其基類默認的實現。所以可以這么說,如果基類中有一個虛擬私有成員函數,表示這是一個“可以”被派生類修改的實現細節。注意,當中的用詞,是“可以”,而不是別的。

最后來看看將classID聲明為virtual protected的情況。將classID聲明為protected表示基類“需要”子類看見這個函數,注意,我使用“需要”這個動詞,這個詞表示了一定的“強制”意味。與將classID聲明為virtual private的情況對比一下,我想你已經知道答案了,即是,如果基類中有一個虛擬保護成員函數,表示這是一個必須被派生類修改的實現細節?!氨仨殹边@個詞表達了強制的意思。

關于“virtual與訪問限定符結合”的問題就討論這么多,你也許說,還漏掉了將classID聲明為virtual public的情況。是的,其實,我并不推薦將虛擬函數聲明為public,盡管這種方式在現在很流行,我推薦將其使用virtual protected來替換,這就說明基類必須另外發布一個幾乎不更改的非虛public接口,在這個接口中調用了virtual protectedvirtual private函數,這樣以來,我們就對類的內部實現作了進一步的隱藏,而這無論是對系統的可擴展性,還是可維護性都是大有幫助的。“虛擬函數應該和數據成員一樣對待――讓他們成為私有的,除非設計需求表明應該有較少的限制。提升它們到更高存取級別比把它們降到更私有的級別更容易些。”

最后,把上面所說的小結一下:

??????基類中的一個普通私有成員函數,表示這是一個不可被更改的實現細節。?
??????基類中的一個虛擬私有成員函數,表示這是一個可以被派生類修改的實現細節。
??????基類中的一個虛擬保護成員函數,表示這是一個必須被派生類修改的實現細節。?
???
???最好不要將虛擬成員函數聲明為public,而是用protected來替換。

三.模板方法模式

在理解了上面所述的內容的情況下,再來理解模板方法模式就非常easy了,模板方法是在GOF的經典大作《設計模式》中闡述了一種模式,該模式定義了一個操作中的算法的骨架,而將一些步驟的實現延遲到子類中,模板方法使得派生類可以不改變一個算法的結構即可重定義算法的某些特定步驟。在這里,我不想再重復解釋這個模式如何實現的,我僅僅舉個例子,這個例子將體現出模板方法中最重要的思想。

假設基類定義的一個算法的骨架由3個步驟完成,其中第一個步驟是該繼承體系中不可被改變的一個步驟,即所有的類對該步驟的實現都是一樣的,那個這個步驟可以設置為非虛的private?;第二個步驟是一個可以被派生類改寫也可以不被改寫的步驟,通過上面的討論知道,可以將其設為virtual private?;第三個步驟是針對每一個派生類的實現都不同,那么這個步驟可以被設為virtual protected,而且,步驟三只能針對特定的派生類才有意義,所以將步驟三也設為純虛函數。如下面的代碼所示:

?

class?BaseTemplate?
{?
private
:?

????
void?step1(void)??//?不可被更改的實現細節?

????{?
?????????
????}?
????
virtual?void?step2(void?)?//?可以被派生類修改的實現細節?

????{?
?????????
????}?

protected
:?
????
virtual?void?step3(void?)?=0;?//?必須被派生類修改的實現細節?????


public:?
????
void?work(void)?//?骨架函數,實現了骨架?

????{?
???????step1()?;?
???????step2()?;?
???????step3()?;?????
????}?
};????


???注意,上例中根本沒有暴露任何虛函數,所有的這一切都是通過work()這個非虛的public接口展現出來的,當我們用一個BaseTemplate指針調用work()時,表面上是一個非虛函數調用,采用靜態綁定,事實上也正是這樣,但是,這個調用的背后隱藏的卻是多態調用,即step2step3動態綁定了??匆?#xff0c;采用模板方法模式,不僅定義了一個算法的骨架,而且把這個骨架的實現的細節作了進一步的封裝。我們可以在模板方法模式中可以這樣設計:

(1)???????如果一個函數作為算法骨架中不可變更的一部分,那么可以將此函數作為基類的私有函數,并且在基類的公共骨架函數中調用該函數,即該函數作為骨架的一個不可更改的實現細節。

(2)???????如果一個函數提供了算法骨架某環節的一個缺省實現,那么可以考慮將該函數作為基類的私有虛函數,表示子類可以改寫它,也可以不改寫它。

(3)???????如果作為算法骨架一部分某個函數要求在子類中擁有不同的實現,那么可以考慮將該函數作為基類的保護(純)虛函數,表示子類必須改寫它。

講到這里,已經差不多了,在結束的時候,提一下語法與語義的聯系。通常,語法是表象,語義是表象后面隱藏的東西,而這些隱藏的語義往往更具有價值。舉個例子,public繼承與private繼承在語法方面似乎沒有什么更多的東西值得探討,它們的區別僅僅在于改變了繼承得到的成員的可見性,但是從語義方面來分析,它們就相差太遠了,private繼承在語義上來講是“通過基類來實現自己”,即是“實現繼承”,在這種繼承關系中,基類和子類的關系是很薄弱的;而public繼承在語義上即是我們所熟知的“IS-A”關系,它體現了基類和子類之間的親密性,也正是這種“IS-A”關系為多態性提供了基礎。

所以,通過表面的語法來挖掘其背后的語義很有意義,就像這篇文章中提到的將訪問限定符與virtual結合起來的語法背后隱藏的語義,挖掘出這些語義,對于我們以后在進行設計時作恰當的抉擇無疑是大有幫助的。

總結

以上是生活随笔為你收集整理的纯虚函数能为private吗?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 午夜视频成人 | 三大队在线观看 | 天天舔天天干天天操 | 91啦丨九色丨刺激 | 91精品国产色综合久久不8 | 偷自拍 | 91精品国产99| 亚洲精品二区 | 久久最新网址 | 伊人春色在线 | 久久久久99精品成人片 | 国产精品一线 | 在线观看99| 午夜极品视频 | 成人国产精品免费观看动漫 | 北岛玲一区二区 | 在线观看欧美日韩 | 美女扒开尿口来摸 | 俄罗斯美女av | 91嫩草网 | a级片免费在线观看 | 国产精品传媒在线 | 公车乳尖揉捏酥软呻吟 | 91精品国产综合久久福利 | 日p免费视频| 伊人久色 | 精品日韩一区二区三区四区 | 蜜臀av性久久久久蜜臀aⅴ四虎 | 视频一区在线免费观看 | 女性向小h片资源在线观看 日本天天操 | 美女被娇喘视频 | 狼友视频国产精品 | 最新国产网站 | 青青草原成人网 | 国产91区| 久久精品日韩无码 | 亚洲欧美乱日韩乱国产 | 亚洲午夜剧场 | 麻豆av电影网 | 欧美一级视频免费 | mm1313亚洲国产精品无码试看 | 成人欧美激情 | 激情视频91 | 人妻体体内射精一区二区 | 毛片视频网站 | av黄在线 | 中文字字幕在线中文乱码电影 | 一本色道久久综合亚洲二区三区 | 国产成人亚洲欧洲在线 | 中文字幕在线观看91 | 精一区二区| 精品人妻无码一区二区性色 | 俄罗斯嫩小性bbwbbw | 国产原创在线 | 懂色av蜜臂av粉嫩av | 午夜精品久久久久久久第一页按摩 | 99热3| 在线免费观看成年人视频 | av天天看| 亚洲国产97在线精品一区 | 精品+无码+在线观看 | 免费的黄色小视频 | 亚洲青草视频 | 日日爽夜夜爽 | 中文字幕88| 成人av免费在线 | 亚洲精品国产精品国自产观看 | 女女互慰揉小黄文 | 午夜精品福利在线观看 | 青青自拍视频 | av在线网页 | 尤物视频在线看 | 黄瓜视频91| 久草小说 | 五月婷在线观看 | 色偷偷噜噜噜亚洲男人的天堂 | 青青青国产精品一区二区 | 美日韩精品 | 精品国产理论 | 国产尤物在线视频 | 日日操日日射 | 欧美成人aaaaⅴ片在线看 | 五月婷婷一区二区 | 看片免费黄在线观看入口 | 偷偷操视频 | 四虎色 | 欧美搞逼视频 | 久久国产传媒 | 国产午夜一级片 | 国产精品美女高潮无套 | 久久久久久蜜桃一区二区 | 青草视频在线观看免费 | a级片黄色| 免费啪啪网 | 9l视频自拍九色9l视频 | 日本三级小视频 | 美女喷液视频 | 国产九色在线播放九色 | www夜夜操 |