從引用傳遞到設計模式 (下)

来源:http://www.cnblogs.com/xinxue/archive/2016/06/02/5429142.html
-Advertisement-
Play Games

上一篇 從引用傳遞到設計模式 (上) 的文末,提到非虛擬介面 NVI 的實現,即將虛函數聲明為保護型或私有型,藉由模板函數模式來實現 。 園友 @KillU 看的很仔細,提出了一個問題:虛函數是 private 類型,繼承可以麽? 答案是:可以 5 實現權和調用權 <Effective C++> 中 ...


  上一篇 從引用傳遞到設計模式 (上) 的文末,提到非虛擬介面 NVI 的實現,即將虛函數聲明為保護型或私有型,藉由模板函數模式來實現 。

  園友 @KillU 看的很仔細,提出了一個問題:虛函數是 private 類型,繼承可以麽? 答案是:可以

5  實現權和調用權

  <Effective C++> 中給的解釋是: 重寫一個虛函數,指的是如何做事情 (how), 而調用一個虛函數,指的是什麼時候做事情 (when)

  NVI 或 模板函數模式中,允許派生類重寫虛函數,賦給派生類的是“實現權” - 即函數功能的實現;但是基類仍然掌握“調用權” - 即什麼時候調用虛函數

  聽起來很是拗口,來看一個實例,還是模板函數模式的實現,只不過是將虛函數聲明為私有的 (private)

5.1  私有虛函數

  基類 AbstractClass 和 派生類 ConcreteClass

class AbstractClass {
public:
    void TemplateMethod();
private:
    virtual void PrimitiveOperation1();
    virtual void PrimitiveOperation2();
};

class ConcreteClass : public AbstractClass {
private:
    void PrimitiveOperation1() override;
    void PrimitiveOperation2() override;
};

  基類 AbstractClass 中,兩個虛函數 PrimitiveOperation1,PrimitiveOperation2 以及 TemplateMethod 的實現

// implementation of private member function
void AbstractClass::PrimitiveOperation1() { std::cout << "Operation1 from AbstractClass !" << std::endl; }
void AbstractClass::PrimitiveOperation2() { std::cout << "Operation2 from AbstractClass !" << std::endl; }

// implementation of non-virtual member function
void AbstractClass::TemplateMethod() 
{ PrimitiveOperation1(); PrimitiveOperation2(); }

  派生類 ConcreteClass 中,重寫兩個虛函數 PrimitiveOperation1PrimitiveOperation2

// override
void ConcreteClass::PrimitiveOperation1() { std::cout << "Operation1 from ConcreteClass !" << std::endl; }
void ConcreteClass::PrimitiveOperation2() { std::cout << "Operation2 from ConcreteClass !" << std::endl; }

  當通過指針或引用調用虛函數時,具體是調用基類還是派生類里的虛函數,取決於動態綁定到該指針或引用的類對象

// call virtual functions in AbstractClass
AbstractClass* p1 = new AbstractClass;
p1->TemplateMethod();

// call virtual functions in ConcretetClass
AbstractClass* p2 = new ConcreteClass;
p2->TemplateMethod();

  輸出的結果如下:

Operation1 from AbstractClass !
Operation2 from AbstractClass !
Operation1 from ConcreteClass !
Operation2 from ConcreteClass !

  這個結果,乍一看並不會立即產生疑問,因為派生類重寫了兩個虛函數,這兩個虛函數也是派生類自己的私有成員函數,調用自己類里的虛函數自然沒什麼問題。

5.2  虛函數不被重寫

  下麵改變一下程式,派生類 ConcreteClass 內不重寫兩個虛函數,只是單純的繼承自基類 AbstractClass,如下所示:

class ConcreteClass : public AbstractClass { };

  執行程式,輸出結果:

Operation1 from AbstractClass !
Operation2 from AbstractClass !
Operation1 from AbstractClass !
Operation2 from AbstractClass !

  這時候疑問便出現了:怎麼派生類 ConcreteClass 居然可以訪問基類 AbstractClass 的私有虛函數? 這可是私有類型

  其實這是一種錯覺,並不是派生類直接調用了基類的私有虛函數,而是派生類的非虛成員函數 ConcreteClass::TemplateMethod ,

  因為繼承自基類的非虛成員函數 AbstractClass::TemplateMethod,從而間接的調用了基類的私有虛函數。

  實際的“調用權”依然牢牢握在基類手中,只不過是基類提供了一個 TempaltMethod 的介面 (interface),可以讓派生類來間接調用而已

5.3  私有非虛函數

  再次修改程式,基類內 PrimitiveOperation1 和 PrimitiveOperation2 不再聲明為虛函數,也即基類從派生類手中收回了函數的“實現權”

class AbstractClass {
public:
    void TemplateMethod();
private:
    void PrimitiveOperation1(); // non-virtual
    void PrimitiveOperation2(); // non-virtual
};

  同時派生類 ConcreteClass 中,聲明自己的私有成員函數 PrimitiveOperation1 和 PrimitiveOperation1,“隱藏”了基類中對應的同名函數

class ConcreteClass : public AbstractClass {
private:
    void PrimitiveOperation1();
    void PrimitiveOperation2();
};

  執行程式輸出結果:

Operation1 from AbstractClass !
Operation2 from AbstractClass !
Operation1 from AbstractClass !
Operation2 from AbstractClass !

  輸出的結果和 5.2 是一樣的,派生類調用 ConcreteClass::TemplateMethod,而 ConcreteClass::TemplateMethod 繼承自 AbstractClass::TemplateMethod,

  因此,實際上,仍然還是基類內的非虛成員函數,調用基類內的私有成員函數

5.4  純虛函數

  重申 5.1 中的一句話:當通過指針或引用調用虛函數時,具體調用基類還是派生類里的虛函數,取決於動態綁定到該指針或引用的類對象基類里的私有成員函數

  可以這麼理解,虛函數涉及的是動態綁定,和它本身是公有、私有還是保護,並無多大關係

  實際中應用中,既然要使用模板方法模式,那就是說必定要在派生類 ConcreteClass 中重寫 PrimitiveOperation1 和 PrimitiveOperation2

  而要保證這兩個函數一定會被派生類重寫,可以將它們聲明為純虛函數,即在函數最末尾加 “= 0”,如下所示:

class AbstractClass {
public:
    void TemplateMethod();
private:
    virtual void PrimitiveOperation1() = 0;
    virtual void PrimitiveOperation2() = 0;
};

  此時,因為基類內申明瞭純虛函數,所有基類 AbstractClass 變成了一個抽象基類(abstarct base class)

  抽象基類只能提供介面 (interface),而不能實例化, 執行下麵代碼是會出錯的:

AbstractClass* p1 = new AbstractClass;  // error !
p1->TemplateMethod();

 

小結:

1)  NVI gives the derived class control over how functionality is implemented, but the base class reserves the right when the function will be called

2)  Derived classes may redefine private inherited virtual functions

3)  Pure virtual functions specify inheritance of interface only

 

參考資料:

 <Effective C++> item 35

 <C++ Primer_5th> 15.4 Abstract Base Classes

 <Design Patterns> Template Method

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.將對象轉換為JSON字元串,返回值為一個JSON字元串 public static String toJson(Object value) { try { return mapper.writeValueAsString(value); } catch (Exception e) { e.pri ...
  • 來自http://blog.csdn.net/cctt_1/article/details/8639903 介面代碼: package myws; import javax.jws.WebParam; import javax.jws.WebService; /** * * @author 帳前卒 ...
  • 初學者 作為初學者,通常情況下,我們都會買一本PHP教材,或者在網上看免費教程,這當然是學習的好途徑。因為,這些書籍和網上的免費教程,基本上都是由淺入深的漸進式教學方式,基礎知識居多,高級知識占少量的部分。這樣,可以讓初學者很快的入門,並且建立信心。 我的唯一的一本PHP入門教材是【PHP與MySQ ...
  • 引言 對於 Python 來說,並不缺少併發選項,其標準庫中包括了對線程、進程和非同步 I/O 的支持。在許多情況下,通過創建諸如非同步、線程和子進程之類的高層模塊,Python 簡化了各種併發方法的使用。除了標準庫之外,還有一些第三方的解決方案,例如 Twisted、Stackless 和進程模塊。本 ...
  • Apache和IIS分別有自己的偽靜態操作方法,那在Servers2003_IIS需要給PHP程式使用偽靜態呢?安裝rewrite插件包。 一、下載rewrite插件包,一般裡面必須有httpd.ini和Rewrite.dll,如圖: 二、一般推薦將偽靜態包放至網站根目錄下,然後在需要偽靜態的網站右 ...
  • 編譯器到底做了什麼實現的虛函數的晚綁定呢?我們來探個究竟。 編譯器對每個包含虛函數的類創建一個表(稱為V TA B L E)。在V TA B L E中,編譯器放置特定類的虛函數地址。在每個帶有虛函數的類 中,編譯器秘密地置一指針,稱為v p o i n t e r(縮寫為V P T R),指向這個對 ...
  • 適用場合: 7.3 工廠模式的適用場合 創建新對象最簡單的辦法是使用new關鍵字和具體類。只有在某些場合下,創建和維護對象工廠所帶來的額外複雜性才是物有所值。本節概括了這些場合。 7.3.1 動態實現 如果需要像前面自行車的例子一樣,創建一些用不同方式實現同一介面的對象,那麼可以使用一個工廠方法或簡 ...
  • http://blog.csdn.net/hil2000/article/details/41261267/ 一.我為什麼要學習go語言 當今已經是移動和雲計算時代,Go出現在了工業向雲計算轉型的時刻,簡單、高效、內 置併發原語和現代的標準庫讓Go語言尤其適合雲端軟體開發(畢竟它就是為此而設計的)。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...