1

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

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

WhereNode定义了与两者相同的所有行为(名称、上次修改时间等),但是,我有一个Node称为getParent()返回 type的方法Dir *。这很好用,因为虽然Dir.h显然需要知道 中的实现规范Node.hNode.h但不需要知道其中的内容,Dir.h所以我可以使用前向声明。伟大的。

但是,我最近决定添加多重继承,这样我就可以在某个时间支持文件系统的“快照”。这些是“实时”Node FileDir类的只读版本,并且由于实时版本可以读取和写入,我让每个实时版本都从其快照对偶继承:

[NodeSnapshot] <------ [Node]
    ^                    ^
    |                    |
    +---[DirSnapshot]<---+---[Dir]
    |                    |
    +---[FileSnapshot]<--+---[File]

因此,Dir继承自两个Nodeand DirSnapshot,并且File继承自两个FileSnapshotand Node。到目前为止,一切看起来都不错,直到我们得到getParent(). 在NodeSnapshot中,我返回一个DirSnapshot *. 没问题,我可以再次使用前向声明。但是,在Node,我想返回Dir *。作为程序员,我知道这Dir是 的子类型DirSnapshot,但是编译器无法知道这一点,因为前向声明中没有嵌入任何有用的信息。

是否可以通知编译器这个前向声明是一个子类,因此它不应该告诉我 of 的返回类型与 of 的返回类型Dir::getParent()不同DirSnapshot::getParent()

4

2 回答 2

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();
于 2012-12-20T11:00:05.743 回答
0

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

   [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)
}
于 2012-12-20T09:25:39.067 回答