我在C ++中有这样的经典钻石问题
A
/ \
B C
\ /
D
我知道这通常可以通过使B和C从A实际上继承来解决。
但是我的问题是A类和B类来自我无法编辑的第三方库,B的A继承没有标记为虚拟。
有没有办法解决这个问题?
感谢您的帮助; - )
答案 0 :(得分:0)
解决此问题的一种简单方法是引入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;
};
俗话说“All problems in computer science can be solved by another level of indirection, except of course for the problem of too many indirections”。当然,编写和维护此适配器可能需要做很多工作。因此,我认为评论你的问题的人可能是正确的,你应该重新审视你的设计。
答案 1 :(得分:0)
如果您无法将库中A的继承更改为virtual
,则无法使用单个A元素作为顶部的钻石。该标准明确允许混合相同基类的虚拟和非虚拟继承:
10.1 / 6:对于类型为C的对象c,类型为V的单个子对象
的每个基础子对象共享虚拟基础 类型为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(我的示例中为Employee
)的组合来定义C(我的示例中为Person
)。 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中提供)。因此,您必须告诉使用条款,对于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 !!!
效果很好:Online demo
<强>结论强>
所以是的,有可能解决这个问题,但这很棘手。正如我所说:这将是汗水和眼泪。
例如,将Colleague
转换为Person
没有问题。但是对于Employee
,您需要提供转换运算符。您必须在Employee
中实施3/5的规则而且您必须处理可能出错的一切(分配失败等等)。它不会是小菜一碟。
所以重新考虑你的设计是值得的,正如Lightness Races in Orbit在评论中所建议的那样: - )