C++ 社區對多重繼承(MI)看法分歧:一方認為單繼承有益則多繼承更有益;另一方認為多繼承引入的麻煩得不償失。本文旨在理清這兩種觀點的核心爭議。
MI 可能從多個基類繼承相同名稱(如函數、typedef),導致調用歧義:
class BorrowableItem { public: void checkOut(); };
class ElectronicGadget { private: bool checkOut() const; };
class MP3Player : public BorrowableItem, public ElectronicGadget { };
MP3Player mp;
mp.checkOut(); // 錯誤:歧義,即使 ElectronicGadget::checkOut 是 private
C++ 先進行最佳匹配,再檢查訪問性。兩個 checkOut 匹配度相同,無最佳匹配,故報歧義錯誤。解決方法是顯式指定作用域:mp.BorrowableItem::checkOut();。
經典的「致命菱形」問題:
class File { ... };
class InputFile : public File { ... };
class OutputFile : public File { ... };
class IOFile : public InputFile, public OutputFile { ... };
IOFile 中應包含幾個 File::fileName?預設會複製兩份(每條路徑一份),但邏輯上通常只需一份。解決方案是使用虛擬繼承:
class InputFile : virtual public File { ... };
class OutputFile : virtual public File { ... };
標準庫中的 iostream 體系(basic_ios, basic_istream, basic_ostream, basic_iostream)即採用此模式。
結合 public inheritance of an interface 和 private inheritance of an implementation:
class IPerson { public: virtual ~IPerson(); virtual std::string name() const = 0; ... };
class PersonInfo { public: virtual const char* theName() const; virtual const char* valueDelimOpen() const; ... };
class CPerson : public IPerson, private PersonInfo {
public:
explicit CPerson(DatabaseID pid) : PersonInfo(pid) {}
virtual std::string name() const { return PersonInfo::theName(); }
private:
const char* valueDelimOpen() const { return ""; } // 重定義去除定界符
const char* valueDelimClose() const { return ""; }
};

此例證明 MI 可以既有用又清晰:CPerson 實現 IPerson 接口,並復用 PersonInfo 的實現,同時通過私有繼承重定義虛函數以滿足接口需求。