覆盖方法返回类型,在C ++中包含不完整的派生类

时间:2012-12-20 08:45:41

标签: c++ multiple-inheritance

我在C ++中编写了一个“文件系统”抽象,具有以下继承层次结构:

 [Node]
   ^
   |
   +----[Dir]
   |
   +----[File]

其中Node定义了与两者相同的所有行为(名称,上次修改的时间等)但是,我有一个名为Node的{​​{1}}方法返回类型{{1 }}。这很好用,因为虽然getParent()显然需要知道Dir *中的实现规范,Dir.h不需要知道Node.h中的内容,所以我可以使用转发宣言。大。

但是,我最近决定添加多重继承,因此我可以在特定时间支持文件系统的“快照”。这些是“实时”Node.h Dir.hNode类的只读版本,由于可以读取和写入实时版本,因此每个实时版本都会继承从它的快照双重:

File

因此,Dir继承自[NodeSnapshot] <------ [Node] ^ ^ | | +---[DirSnapshot]<---+---[Dir] | | +---[FileSnapshot]<--+---[File] DirNode继承自DirSnapshotFile。到目前为止,一切看起来都很好,直到我们得到FileSnapshot的声明。在Node中,我返回getParent()。没问题,我可以再次使用前方声明。但是,在NodeSnapshot中,我想返回DirSnapshot *。作为程序员,我知道NodeDir *的子类型,但编译器无法知道这一点,因为前向声明中没有嵌入任何有用的信息。< / p>

是否可以通知编译器此前向声明是一个子类,因此它不应该告诉我Dir的返回类型与DirSnapshot的返回类型不一致?

2 个答案:

答案 0 :(得分:2)

虽然解决方案往往很冗长,但可以在没有任何语言支持的情况下实现/模拟返回类型协方差。另一方面,相互递归定义没有问题。需要使用调用虚拟私有函数的非虚拟公共(内联)函数。这是一种有用的技术,有些甚至认为所有接口都应该像这样实现。

以下是一个例子:

// forward declare classes
class A;
class B;
class AA;
class BB;

// parents
class A
{
    virtual B* getB_impl();
public:
    B* getB() { return getB_impl(); }
};
class B
{
    virtual A* getA_impl();
public:
    A* getA() { return getA_impl(); }
};

// kids
class AA : public A
{
    virtual BB* getBB_impl();
    B* getB_impl();
public:
    BB* getB() { return getBB_impl(); }
};
class BB : public B
{
    virtual AA* getAA_impl();
    A* getA_impl();
public:
    AA* getA() { return getAA_impl(); }
};

// implement parents
B* A::getB_impl() { return new B; }
A* B::getA_impl() { return new A; }

// implement kids
B* AA::getB_impl() { return getBB_impl(); }
BB* AA::getBB_impl() { return new BB; }
A* BB::getA_impl() { return getAA_impl(); }
AA* BB::getAA_impl() { return new AA; }

// check
A a; B b;
A* pa; B* pb;
AA aa; BB bb;
AA* paa; BB* pbb;

pa = b.getA();
pb = a.getB();

pa = bb.getA();
pb = aa.getB();

paa = bb.getA();
pbb = aa.getB();

答案 1 :(得分:0)

您应该从项目中排除多重继承。 快照与文件系统/目录/文件的当前状态相关联,因此它的数据也是如此。 当您不希望复制功能和继承文件时,您需要继承,并且可以接受来自节点的目录。但是,由于快照是数据,因此您可能需要将节点数据移动到某个结构,特殊文件/目录数据到其他结构,所有这些都将在某些函数调用中返回或保存/恢复,这些函数将因继承而过载。 / p>

   [Node]       [Node Data]
   ^
   |
   +----[Dir]   [Node Data][Dir Special Data]
   |
   +----[File]  [Node Data][File Special Data]

virtual void Node::Snapshot()
{
//some code operating with Node Data (saving on disk for ex)
}
virtual void Dir::Snapshot()
{
Node::Snapshot();
//some code operating with Special Dir Data (saving on disk for ex)
}
virtual void File::Snapshot()
{
Node::Snapshot();
//some code operating with Special File Data (saving on disk for ex)
}