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

来源:http://www.cnblogs.com/xinxue/archive/2016/04/25/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;
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
更多相關文章
  • 概述 Java語言中,提供了一套數據集合框架,其中定義了一些諸如List、Set等抽象數據類型,每個抽象數據類型的各個具體實現,底層又採用了不同的實現方式,比如ArrayList和LinkedList。 除此之外,Java對於數據集合的遍歷,也提供了幾種不同的方式。開發人員必須要清楚的明白每一種遍歷 ...
  • 程式中需USE COMOBJ單元 1.Q:如何得到機器上IIS中所有的WEB虛擬站點. A: var InstallPath: String; WebSite, WebServer, WebRoot: Variant; count: Integer; Flag: Boolean; begin Fla ...
  • 本文目的 PHP的全局錯誤處理,在開發項目的時候很有用,可以幫助開發者快速定位一些問題,提高工作效率。預設情況下,全局錯誤會直接輸出,但是最近開發時使用的一個框架庫對全局錯誤處理進行了設定,導致很多錯誤信息沒有輸出,在定位問題上有一定的耗時。所以,研究了一下此庫的實現,發現它設定了error_rep ...
  • 什麼是Queue集合? 答:Queue用於模擬隊列這種數據結構。隊列通常是指“先進先出(FIFO)”的容器。隊列的頭部保存在隊列中存放時間最長的元素,尾部保存存放時間最短的元素。新元素插入到隊列的尾部,取出元素會返回隊列頭部的元素。通常,隊列不允許隨機訪問隊列中的元素。 Queue介面中定義瞭如下的 ...
  • “這裡要用char類型”; “這裡要用int類型”; “其實實現這個方法只需要把另一個方法的返回值的類型和傳入參數的類型改成float類型就實現了”; “其實這個演算法只需要把以前寫的那個稍微改動一下就行了”; ……………… 學過面向對象語言的都知道GP這個概念,就是泛型程式設計,說的再明白點就是編寫 ...
  • Java併發編程系列【未完】: Java 併發編程:核心理論 Java併發編程:Synchronized及其實現原理 Java併發編程:Synchronized底層優化(輕量級鎖、偏向鎖) 一、重量級鎖 上篇文章中向大家介紹了Synchronized的用法及其實現的原理。現在我們應該知道,Synch ...
  • 微信支付介面文檔:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_1 首先你得知道這個jsapi是不能離開微信進行調用支付的,明白了這個道理我們好下手,頁面是在微信內顯示並通過jsapi調用微信支付組件進行支付。 可以看看我們上一 ...
  • 下載頁: http://www.rabbitmq.com/install-standalone-mac.html 1、下載頁面首部的文件(頁面下載可能比較慢,使用迅雷下載就好),之後解壓到一個合適的路徑(例如:/Users/enniu1/Desktop/zjg/)。 2、配置命令訪問路徑 cd ~ ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...