簡體   English   中英

第三方圖書館的鑽石繼承

[英]Diamond inheritance with a third party library

我在C ++中遇到這樣的經典鑽石問題

  A
 / \
B   C
 \ /
  D

我知道通常可以通過使B和C實際上從A繼承來解決。

但是我的問題是,類A和B來自我無法編輯的第三方庫,並且B從A的繼承未標記為虛擬。

有辦法解決嗎?

謝謝您的幫助 ;-)

解決此問題的一種簡單方法是引入Adapter類。 這樣,層次結構變為

  A
 / 
B  AdapterC
 \ /
  D

而且AdapterC的代碼看起來像

class AdapterC
{
public:
    explicit AdapterC(C c) : c(std::move(c)) {}
    operator C& () { return c; } //Maybe this should be explicit too...
    /** Interface of C that you want to expose to D, e.g.
      int doSomething(double d) { return c.doSomething(d); }
    **/
private:
    C c;
};

俗話說,“ 計算機科學中的所有問題都可以通過間接的另一層次解決,當然,過多的間接問題除外 ”。 當然,編寫和維護此適配器可能需要大量工作。 因此,我認為對您的問題發表意見的人可能是正確的,因此您應該重新設計。

關鍵設計問題

如果您無法將庫中A的繼承更改為virtual ,則無法使單個A元素成為頂部的菱形。 該標准明確允許混合使用同一基類的虛擬和非虛擬繼承:

10.1 / 6:對於類型為C的對象c,類型為V的單個子對象由c的每個具有虛擬基礎類型V的基本子對象共享 。(...)。
10.1 / 7:一個類可以具有給定類型的虛擬和非虛擬基類。

例:

namespace mylib {  // namesape just to higlight the boundaries of the library
    struct Person {                              // A
        static int counter; 
        int id; 
        Person() : id(++counter) {}
        void whoami() { cout << "I'm "<<id<<endl; }
    };  //A
    struct Friend: Person {};                    //B -> A
    int Person::counter=0; 
}
struct Employee : virtual mylib::Person {};      // C->A
struct Colleague : Employee, mylib::Friend {};   // D->(B,c)
...
mylib::Friend p1;   // ok !  
p1.whoami(); 
Employee p2;        // ok !
p2.whoami(); 
Colleague p3;       // Attention: No diamond ! 
//p3.whoami();      // ouch !! not allowed: no diamond so for which base 
                    // object has the function to be called  ? 
p3.Employee::whoami();       // first occurrence of A
p3.mylib::Friend::whoami();  // second second occurrence of A

在線演示

替代設計

由於您無法干預外部庫,因此必須以不同的方式組織事情。 但是無論您如何做,都會流着汗和流淚。

您可以通過使用A(示例中的Person )的組成來定義C(示例中的Employee )。 A子對象將被創建,或者在特殊情況下將從另一個對象接管。 您需要努力復制A的接口,並將調用轉發到A子對象。

總體思路如下:

class Employee { 
    mylib::Person *a;
    bool owna;
protected:
    Employee (mylib::Person& x) : a(&x), owna(false) { }  // use existing A
public: 
    Employee () : a(new mylib::Person), owna(true) { }  // create A subobject   
    ~Employee () { if (owna) delete a; }
    void whoami() { a->whoami(); } // A - fowarding 
};

如果這樣做,則可以使用構造函數中的一個技巧定義具有多重繼承的D:

struct Colleague : mylib::Friend, Employee { 
    Colleague () : mylib::Friend(), Employee(*static_cast<Person*>(this)) {}; 
    using Friend::whoami; 
};

唯一的問題將是A接口的成員函數(如上所述,已在C中提供)的歧義。 因此,您必須使用using子句告訴您,對於A,您通過B而不是C。

最后,您可以使用以下命令:

Employee p2; 
p2.whoami(); 

Colleague p3;   // Artifical diamond ! 
p3.whoami();  // YES !!  
p3.Employee::whoami();   // first occurence of A
p3.mylib::Friend::whoami(); // second second occurence of A
                             // all whoami refer to the same A !!! 

效果很好: 在線演示

結論

是的,有可能解決此問題,但這非常棘手。 就像我說的:會流汗和流淚。

例如,您可以Colleague地將Colleague轉換為Person 但是對於Employee ,您需要提供轉換運算符。 您必須在Employee實施3/5規則,並且必須處理所有可能出錯的事情(分配失敗等)。 這不會是小菜一碟。

因此,真的很值得重新考慮您的設計,就像在評論中建議的“ 軌道中的競速” :-)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM