在我的C++项目中,我经常遇到以下情况,其中我有一个类A
,它有一个指向另一个类的接口X
的指针。该类A
被扩展为处理X
的子类型。这种情况再次发生。
请注意,在每一层,接口都会发生变化(添加了新的对象方法,并重载了其他一些方法),因此在A
的每个子类中,指向X
的指针被转换为X
的子类
+----------+ +----------+
| | A has X | |
| A +-------------------> X |
+-----+----+ +-----+----+
| |
| |
| |
+-----+----+ +-----+----+
| | | |
| B +-------------------> Y |
+-----+----+ +-----+----+
| |
+-----+----+ +-----+----+
| | | |
| C +-------------------> Z |
+----------+ +----------+
我想知道是否有任何模式可以处理这种情况,或者它本质上是一个糟糕的设计?
EDIT,考虑以下结构:
+----------+ +----------+ +----------+
| | | | A has X | |
| M <----------+ A +-------------------> X |
+-----+----+ +-----+----+ +-----+----+
| | |
| | |
| | |
+-----+----+ +-----+----+ +-----+----+
| | | | | |
| N <----------+ B +-------------------> Y |
+-----+----+ +-----+----+ +-----+----+
| | |
+-----+----+ +-----+----+ +-----+----+
| | | | | |
| L <----------+ C +-------------------> Z |
+----------+ +----------+ +----------+
这实际上就是应用程序的外观。每个级别代表我们多层框架中的一层。
考虑一下这个实现:
struct X {};
struct Y: X {};
struct Z: X {};
class A
{
// todo: add public interface
protected:
std::unique_ptr<X> x_;
};
template<typename T>
class AImpl: public A
{
protected:
T* get() { return dynamic_cast<T*>(x_.get()); }
};
class B: public AImpl<Y>
{
public:
// todo: add public interface
using AImpl<Y>::get; // implement rest of B in terms of this
};
class C: public AImpl<Z>
{
public:
// todo: add public interface
using AImpl<Z>::get; // implement rest of C in terms of this
};
编辑(因为C需要从B继承)
考虑将B和C定义如下:
template<typename T>
class BImpl: public AImpl<T>
{
public:
// todo: add public interface
using AImpl<T>::get; // implement rest of B in terms of this
};
// this is a new thing (see my explanation on this, below)
using B = BImpl<Y>;
class C: public BImpl<Z>
{
public:
// todo: add public interface
using AImpl<Z>::get; // implement rest of C in terms of this
};
但是,实现这样的B有一个问题:让类做不止一件事是糟糕的设计。在这种情况下,B必须既是基类(对于C)又是具体类型。
我创建了一个B实现类作为基础。然后,定义B可以是类型别名(我在上面使用的语句),也可以是具体的类型专门化:
class B: public BImpl<Y> {};
这里的问题被称为"钻石问题"(请参阅维基百科),它可以通过使用虚拟继承来解决(请参阅本文)。
class InterfaceA
{};
class TypeA: public virtual InterfaceA
{};
class InterfaceB: public virtual InterfaceA
{};
class TypeB: public TypeA, public InterfaceB
{};
不要使用interpret_cast进行强制转换,即从TypeB到InterfaceA(虚拟继承与直接继承具有不同的内存布局)