我正在使用的外部库具有以下结构:
#include <stdio.h>
struct A {
protected:
virtual void f() {}
};
struct B : A {
void f() {printf("B\n");}
};
我现在已经通过
扩展了这个库struct C : A {
void f() {printf("C\n");}
};
现在我希望struct D : A
使用B
或C
的{{1}},具体取决于运行时可用的信息。我无法修改库,f()
继承C
是不切实际的,因为B
比B
复杂得多。这就是我想出的:
C
我在MacOSX上测试了它,它可以正常使用g ++和clang ++。但它通常安全吗?如果没有,有没有更好的方法呢?
答案 0 :(得分:1)
不,你所拥有的是不安全的。 B
和C
不会从_A
继承,因此将其视为未定义行为。它可能会起作用,它可能会崩溃,它可能会在网上订购披萨,所有这些都取决于月球的当前阶段。所以不要这样做。
而且我相信你不必。以下应该有效:
struct BB : B
{
using B::f; // Make it public
};
struct D : A
{
D(char x) {
switch (x) {
case 'B': b.reset(new BB()); break;
case 'C': c.reset(new C()); break;
}
}
void f()
{
if (b) b->f();
else c->f();
}
std::unique_ptr<BB> b;
std::unique_ptr<C> c;
};
这个想法是保持最多一个指针非空(或找到另一种方法来确定你是BB
还是C
- boost::variant
也可能有用)。
请注意,名称_A
对于用户代码是非法的。以下划线后跟大写字母开头的标识符保留给编译器和标准库。
答案 1 :(得分:1)
不,不是。
您正在将B投射到_A,这可能会在此过程中发生变化。目前_A与A相同的事实只是一个你不能依赖的巧合。
如果您的目标是访问受保护的功能,则可以使用pImpl方法:
struct _Abstract {
virtual void doF()=0;
}
struct _B : B, _Abstact {
void doF(){f();};
}
struct _C : C, _Abstract {
void doF(){f();};
}
struct D {
D (_C* impl)
{
pImpl = impl;
}
D (_B* impl)
{
pImpl = impl;
}
void f() { pImpl->dooF();};
private:
_Abstract* pImpl;
}
然后你可以
D* b = new D(new _B());
D* c = new D(new _C());
b->f();
c->f();
答案 2 :(得分:0)
@MichaelCMS'的答案似乎是最好的,因为_Abstract
的继承使我的粗略演员正式正确。另外,与@ Angew的答案相反,如果有许多类如B
和C
,它会很好地扩展。我不得不稍微修改它以适用于我的例子:
struct _Abstract {
virtual void _f() = 0;
virtual ~_Abstract() {}
};
template <class T>
struct _A : T, _Abstract {
// bypass protected
void _f() {T::f();}
};
struct D : A {
D(char x) {
switch (x) {
case 'B': p = new _A<B>(); break;
case 'C': p = new _A<C>(); break;
}
}
~D() {delete p;}
void f() {p->_f();}
_Abstract* p;
};
显然,如果图书馆设计师将f()
公开,整个问题就会消失......所以请图书馆设计师公开你的方法!你无法预见所有的用例,你只是强迫我们通过或多或少粗略的方法绕过你的受保护的(甚至私人的)...
修改强>
在实践中,双继承解决方案不能很好地工作,因为它导致了我的案例中的钻石继承问题,这使问题变得非常复杂。受@Charles Bailey对Accessing protected member of template parameter的回答的启发我提出了这个问题:
struct U : A {
typedef void (A::*f_t)();
static inline f_t _f() {return &U::f;}
};
struct D : A {
D(char x) {
switch (x) {
case 'B': p = new B(); break;
case 'C': p = new C(); break;
}
}
~D() {delete p;}
void f() {(p->*U::_f())();}
A* p;
};
这直接解决了问题的根源,即将方法设置为受保护而不是公共,同时不会使继承情况不必要地复杂化。由于这只是通过成员函数指针删除受保护属性的技巧,因此它应该是安全的: - )