1

次の継承階層を使用して、C++ で「ファイルシステム」の抽象化を作成しています。

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

whereNodeは両方と同じすべての動作を定義します (名前、最終更新時刻など)。ただし、type を返すNodeメソッドが呼び出されています。の実装仕様を明らかに知る必要がありますが、何が入っているかを知る必要がないため、前方宣言を使用できるため、これはうまく機能します。偉大な。getParent()Dir *Dir.hNode.hNode.hDir.h

しかし、最近、特定の時点でのファイルシステムの「スナップショット」をサポートできるように、複数の継承を追加することにしました。これらは「ライブ」Node FileおよびDirクラスの読み取り専用バージョンであり、ライブ バージョンは書き込みだけでなく読み取りもできるため、各ライブ バージョンはスナップショット デュアルから継承します。

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

したがって、は と のDir両方から継承し、 は と のNode両方から継承します。の宣言に到達するまで、これまでのところすべてが順調に見えます。では、 を返します。問題ありません。再度前方宣言を使用できます。しかし、で、私は戻りたいです。私はプログラマとして、が のサブタイプであることを知っていますが、前方宣言にはこの有用な情報が埋め込まれていないため、コンパイラはこれを知る方法がありません。DirSnapshotFileFileSnapshotNodegetParent()NodeSnapshotDirSnapshot *NodeDir *DirDirSnapshot

この前方宣言がサブクラスであることをコンパイラに通知することは可能ですか?したがって、 の戻り値の型が の戻り値の型と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 に答える