首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

C++ 雙重分派與訪問者模式:從編譯時重載到運行時多態的演進

     編輯:關於C++

問題起因

代碼審查中同事建議:「使用了多個 instanceof,這是雙重分派的跡象,可考慮訪問者模式。」由此引發對 Double Dispatch 與 Visitor Pattern 的深度探索。

單分派 vs 雙重分派

  • 單分派:C++ 虛函數調用,根據 this 對象的動態類型決定執行邏輯(一次動態綁定)。
  • 雙重分派:需根據 兩個 對象的動態類型(如 Sales 和 Vehicle)共同決定邏輯。

嘗試 1:函數重載失敗

在 Sales 中提供 GetDiscountRate(Vehicle&) 和 GetDiscountRate(Benz&) 重載。但 重載解析發生在編譯期,參數靜態類型為 Vehicle& 時永遠調用前者,無法實現運行時雙重分派。

嘗試 2:雙重虛函數調用(手工雙重分派)

在 Vehicle 增加 GetManagerDiscountRate() 虛函數,Benz 重寫之。SalesManager::GetDiscountRate 調用該虛函數。實現了:

  1. 第一次分派:Sales/SalesManager 的虛函數區分銷售角色
  2. 第二次分派:Vehicle/Benz 的虛函數區分車型

雖可行,但擴展性差:新增車型需修改所有銷售類,新增銷售角色需修改所有車類。

標準解法:訪問者模式

利用 編譯期知道 *this 類型 的特性,反向調用:

// 銷售基類
class ISales {
public:
    virtual double Accept(IVehicle& v) = 0;
};
class SalesManager : public ISales {
    double Accept(IVehicle& v) override { return v.Visit(*this); } // *this 編譯期類型為 SalesManager
};

// 車輛接口
class IVehicle {
public:
    virtual double Visit(Sales&) = 0;
    virtual double Visit(SalesManager&) = 0;
};
class Benz : public IVehicle {
    double Visit(Sales&) override { return 0.06; }
    double Visit(SalesManager&) override { return 0.066; }
};

訪問者模式擴展性分析

操作難度說明
新增車型 (如 Fiat)容易繼承 IVehicle 實現兩個 Visit 即可,無需修改現有代碼
新增銷售角色困難需修改 IVehicle 和所有車型類,添加新的 Visit 重載

此為「表達式問題」體現。進階模式如 Acyclic Visitor 可雙向擴展,但複雜度較高。

總結

C++ 無原生雙重分派支持,因語言設計權衡。訪問者模式是 C++ 中模擬雙重分派的標準手法,通過「雙重虛函數調用」將運行時類型信息傳遞給編譯期重載解析。理解其本質有助於在面對多維度多態決策時做出正確架構選擇。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved