我在C ++中编写了一个“文件系统”抽象,具有以下继承层次结构:
[Node]
^
|
+----[Dir]
|
+----[File]
其中Node
定义了与两者相同的所有行为(名称,上次修改的时间等)但是,我有一个名为Node
的{{1}}方法返回类型{{1 }}。这很好用,因为虽然getParent()
显然需要知道Dir *
中的实现规范,Dir.h
不需要知道Node.h
中的内容,所以我可以使用转发宣言。大。
但是,我最近决定添加多重继承,因此我可以在特定时间支持文件系统的“快照”。这些是“实时”Node.h
Dir.h
和Node
类的只读版本,由于可以读取和写入实时版本,因此每个实时版本都会继承从它的快照双重:
File
因此,Dir
继承自[NodeSnapshot] <------ [Node]
^ ^
| |
+---[DirSnapshot]<---+---[Dir]
| |
+---[FileSnapshot]<--+---[File]
和Dir
,Node
继承自DirSnapshot
和File
。到目前为止,一切看起来都很好,直到我们得到FileSnapshot
的声明。在Node
中,我返回getParent()
。没问题,我可以再次使用前方声明。但是,在NodeSnapshot
中,我想返回DirSnapshot *
。作为程序员,我知道Node
是Dir *
的子类型,但编译器无法知道这一点,因为前向声明中没有嵌入任何有用的信息。< / p>
是否可以通知编译器此前向声明是一个子类,因此它不应该告诉我Dir
的返回类型与DirSnapshot
的返回类型不一致?
答案 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)
}