青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

woaidongmao

文章均收錄自他人博客,但不喜標題前加-[轉貼],因其丑陋,見諒!~
隨筆 - 1469, 文章 - 0, 評論 - 661, 引用 - 0
數據加載中……

純虛函數能為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函數,這樣以來,我們就對類的內部實現作了進一步的隱藏,而這無論是對系統的可擴展性,還是可維護性都是大有幫助的。“虛擬函數應該和數據成員一樣對待――讓他們成為私有的,除非設計需求表明應該有較少的限制。提升它們到更高存取級別比把它們降到更私有的級別更容易些?!?span style="color: black">

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

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

三.模板方法模式

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

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

 

class BaseTemplate
{
private:

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

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

public:
   
void work(void) // 骨架函數,實現了骨架 
    {
       step1() ;
       step2() ;
       step3() ;    
    }
};   


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

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

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

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

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

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


--zhuweisky 2003.04.18

Feedback

# re: 純虛函數能為private嗎?  回復  更多評論   

2006-04-16 13:32 by lkhg

hk,nnb,nb,khgdfmgsdkkopx,cv flkgbp[fdl p[lgp[dlfp[dsgfsdgbvs

# re: 純虛函數能為private嗎?  回復  更多評論   

2006-04-16 18:51 by ZiDing

嗯,不錯不錯。

# re: 純虛函數能為private嗎?  回復  更多評論   

2006-04-17 17:48 by 千里馬肝

LZ的論點即是《Exceptional C++ Style中文版》的第17、18、19節 的進一步描述

# re: 純虛函數能為private嗎?  回復  更多評論   

2006-05-02 21:51 by 田地

關于基類中的一個虛擬保護成員函數,protected表示基類需要子類看見這個函數,也就是派生類的其他函數可以調用這個虛擬函數吧?派生類可以修改也可以不修改吧?如果是必須修改,只能依靠聲明為純虛函數來強制吧?

# re: 純虛函數能為private嗎?  回復  更多評論   

2006-10-12 09:43 by dxt

受用,不錯

# re: 純虛函數能為private嗎?  回復  更多評論   

2006-11-04 14:24 by mumutou

我覺得他說的不對,
class BaseTemplate
{
private:

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

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

}

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

public:
void work(void) //
骨架函數,實現了骨架
{
step1() ;
step2() ;
step3() ;
}
};
protected:
virtual void step3(void ) =0; //
必須被派生類修改的實現細節
這是一個pure virtual 函數,當然必須被派生類修改了,不然怎么用

# re: 純虛函數能為private嗎?  回復  更多評論   

2006-11-04 14:32 by mumutou

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

class Base
{
protected:
virtual 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
{
protected:
void doWork()
{

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


void main(int argc, char* argv[])
{
DerivedA* bp = new DerivedA() ;
bp->work() ;
delete bp ;
}
基類中的一個虛擬保護成員函數,表示這是一個必須被派生類修改的實現細節。
不然的話,調用的還是Base classID
這樣理解就對了

# re: 純虛函數能為private嗎?  回復  更多評論   

2006-12-17 21:59 by 天吻

很受用.頂啊!!!

# re: 純虛函數能為private嗎?  回復  更多評論   

2007-01-17 15:33 by 黃劍雄

"講到這里就要提一下,如果當將classID聲明為public/protected,并且子類也定義同名的函數classID,但是子類的classID與基類的classID的函數簽名不同,那么此時發生的將是函數重載而不是覆蓋。"

這句話是什么意思?為什么是重載,派生類的函數能重載基類的函數??
重載是對同一個類域的不同函數來說吧

# re: 純虛函數能為private嗎?  回復  更多評論   

2007-02-01 11:29 by zhaoflying

@黃劍雄
樓主寫錯了,不是重載,而是隱藏。事實上子類會把基類中的這個同名函數給隱藏掉,試圖通過子類對象來調用基類的classID()會報編譯錯誤,但可以通過子類對象調用自己的classID()。所以這種做法是不合適的。

# re: 純虛函數能為private嗎?  回復  更多評論   

2007-07-14 16:05 by juv

樓主內容相當精彩,但是呢,如同樓上所說:不是覆蓋和重載,是隱藏!

還有:
基類中的一個虛擬私有成員函數,表示這是一個可以被派生類修改的實現細節。
基類中的一個虛擬保護成員函數,表示這是一個必須被派生類修改的實現細節。


不敢茍同!建議樓主看EFFETIVE C++ 第三版條款34!

# re: 純虛函數能為private嗎?  回復  更多評論   

2007-09-20 18:04 by asd

派生類public繼承時,基類成員函數private還是public沒有區別,摟主混淆概念了!

# re: 純虛函數能為private嗎?  回復  更多評論   

2007-12-01 22:11 by gules

virtual函數的訪問控制級別具有隱含的意義:一個protected virtual function告訴你:你寫的派生類應該,可是說是必須調用我(基類)的實現。而一個private virtual function是在說:派生類可以覆蓋,也可以不覆蓋我,隨你的便。但是你不可以調用我的實現。

 

posted on 2010-07-01 00:33 肥仔 閱讀(1946) 評論(2)  編輯 收藏 引用 所屬分類: C++ 基礎

評論

# re: 純虛函數能為private嗎?  回復  更多評論   

如果沒有遇到這樣的需求,講太多是無益的,需求是什么?然后按這個需求去設計框架。樓主有這方面的需求拿來看看嗎?
2010-07-01 01:57 | chaogu

# re: 純虛函數能為private嗎?  回復  更多評論   

大牛!謝謝
2014-08-27 17:46 | 陳小輝
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美激情精品久久久久久变态| 欧美大成色www永久网站婷| 日韩一级精品视频在线观看| 欧美久久久久免费| 在线中文字幕日韩| 亚洲色图综合久久| 国产一区二区| 欧美国产日韩二区| 欧美日韩一区二区精品| 午夜视频在线观看一区二区| 欧美一区激情| 最新精品在线| 一区二区三区久久网| 国产香蕉97碰碰久久人人| 久久躁日日躁aaaaxxxx| 欧美va日韩va| 欧美一区二区三区四区高清| 玖玖玖国产精品| 一区二区三区免费在线观看| 午夜精品一区二区三区在线| 亚洲欧洲日产国码二区| 亚洲午夜免费福利视频| 亚洲电影毛片| 亚洲一区日韩| 亚洲精品在线视频| 欧美一区亚洲一区| a4yy欧美一区二区三区| 欧美伊人影院| 亚洲一区www| 欧美~级网站不卡| 久久不射网站| av72成人在线| 蜜月aⅴ免费一区二区三区 | 美国成人直播| 欧美性猛交xxxx免费看久久久 | 亚洲线精品一区二区三区八戒| 午夜免费日韩视频| 一区二区三区四区国产精品| 久久久久久夜| 久久精品国产99国产精品澳门| 欧美乱在线观看| 久久综合成人精品亚洲另类欧美| 欧美日一区二区在线观看| 欧美成人69av| 狠狠色狠色综合曰曰| 亚洲一区二区网站| 99亚洲一区二区| 欧美成人免费全部| 欧美mv日韩mv国产网站| 国产日韩亚洲欧美| 亚洲一区亚洲| 午夜精品福利一区二区三区av| 欧美精品1区| 亚洲第一在线视频| 国产日韩在线看| 亚洲欧美综合v| 欧美在线观看日本一区| 国产日本亚洲高清| 亚洲制服欧美中文字幕中文字幕| 亚洲欧美激情四射在线日| 欧美日韩国产高清| 日韩视频在线免费观看| 一本一道久久综合狠狠老精东影业 | 国产视频久久久久| 小黄鸭精品aⅴ导航网站入口| 午夜在线视频一区二区区别| 欧美天堂亚洲电影院在线观看 | 久久精品女人| 精品动漫一区二区| 久久久夜夜夜| 亚洲国产岛国毛片在线| 亚洲美女免费视频| 欧美日韩国产成人在线观看| 亚洲网站在线观看| 欧美资源在线观看| 怡红院精品视频| 欧美成人69av| 日韩一级黄色片| 久久国产婷婷国产香蕉| 在线电影欧美日韩一区二区私密| 久久在线免费观看视频| 欧美激情一区在线| 亚洲男同1069视频| 国内精品久久久久久久97牛牛| 久久久亚洲人| 日韩一级裸体免费视频| 国产精品99久久久久久有的能看| 国产精品毛片| 久久人体大胆视频| 日韩一级黄色av| 久久久久久亚洲精品杨幂换脸| 欧美在线视频免费| 这里只有精品丝袜| 国模吧视频一区| 欧美精品一区在线播放| 亚洲一区二区网站| 亚洲福利视频一区| 午夜国产精品影院在线观看| 一区二区三区在线视频免费观看| 欧美国产成人精品| 欧美亚洲免费在线| 亚洲精品黄色| 噜噜噜在线观看免费视频日韩| 一区二区三区www| 国产在线欧美| 欧美视频在线观看免费| 久久久久久网站| 亚洲伊人第一页| 91久久精品国产91久久性色| 久久久久99精品国产片| 亚洲一级黄色片| 亚洲精品国精品久久99热一| 国产日产欧美精品| 欧美日韩中字| 欧美激情免费观看| 久久久久国产精品午夜一区| 一本色道久久综合一区| 欧美激情日韩| 你懂的网址国产 欧美| 欧美一区二区三区播放老司机| 99re66热这里只有精品3直播| 精品动漫3d一区二区三区免费| 国产精品激情偷乱一区二区∴| 欧美成年人视频网站| 久久精品一二三| 久久国产精品99国产| 亚洲一区视频在线观看视频| 正在播放欧美一区| 亚洲免费电影在线观看| 亚洲第一二三四五区| 免费日韩av电影| 久热精品视频| 美日韩精品免费| 久久综合色天天久久综合图片| 欧美在线视频a| 欧美淫片网站| 久久精品国产在热久久| 欧美中文在线免费| 久久高清一区| 久久久另类综合| 久久久精品2019中文字幕神马| 久久成人这里只有精品| 欧美伊人久久久久久久久影院| 欧美综合激情网| 久久男人资源视频| 久久天天综合| 欧美成人午夜激情视频| 欧美高清不卡在线| 亚洲高清视频在线观看| 亚洲激情偷拍| 中文精品视频| 欧美一级久久| 久久三级福利| 欧美日韩不卡一区| 欧美午夜片欧美片在线观看| 国产精品视频内| 国内自拍亚洲| 91久久精品日日躁夜夜躁欧美| 日韩视频一区二区三区| 亚洲一区免费网站| 久久国产精品一区二区| 免费一级欧美片在线播放| 亚洲第一天堂无码专区| 一本大道av伊人久久综合| 亚洲影视在线播放| 久久久久久久综合| 欧美理论大片| 国产亚洲一区二区三区在线播放| 在线观看的日韩av| 99精品国产高清一区二区 | 久久久午夜电影| 午夜精品视频网站| 久久久综合激的五月天| 免费在线欧美视频| 欧美日韩在线一区二区| 国产一区二区在线观看免费播放| 亚洲国产日韩在线一区模特| a91a精品视频在线观看| 久久久蜜臀国产一区二区| 亚洲国产日韩欧美在线动漫| 亚洲伊人网站| 欧美精品久久久久久| 国产嫩草影院久久久久| 亚洲日本va午夜在线影院| 午夜精品一区二区三区电影天堂| 美日韩精品视频| 亚洲欧美日本国产专区一区| 嫩草国产精品入口| 国产一区日韩二区欧美三区| 夜夜嗨av一区二区三区中文字幕| 久久久精品国产免大香伊 | 久久久一二三| 一本综合久久| 欧美激情亚洲自拍| 激情久久综合| 久久精品视频播放| 一区二区三区欧美| 欧美日韩一级视频| 亚洲高清视频在线观看| 久久久久久久久久久久久女国产乱 |