[英]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.