1

次のクラスのセットがあります。

ここに画像の説明を入力してください

そして、次のコード:

A* a;
if(condition)
{
    a = new E();
}
else
{
    a = new D();
}

のような関数があるとすると、それを呼び出すには、またはのいずれかにキャストする必要F::foo()があります。aE*D*

if(condition)
{
    ((E*)a)->foo();
}
else
{
    ((D*)a)->foo();
}

私の知る限り、はタイプであるため、呼び出すためにキャストaすることは違法です。そして私には、呼び出す前に条件をチェックすることは、設計上の問題のように聞こえます。このクラス階層を改善する方法について、誰かアドバイスをいただけますか?F*F::fooaA*foo

psこのツールを使用して図を描きました。

4

4 に答える 4

5
#include <iostream>

struct A { virtual ~A() {} };

struct C : virtual A {};

struct B : virtual A {};

struct F {
    virtual void Foo() { std::cout << "ok\n"; }
};

struct E : C, virtual F {};

struct D : B, virtual F {};


int main() {
    A *a = new E();
    dynamic_cast<F*>(a)->Foo();
}
  • 混乱し、の参照がaのインスタンスではない場合F、dynamic_castはnullを返します
  • 仮想継承を使用しない場合、あいまいな基本クラスになってしまう可能性があります。あいまいなベースへのAdynamic_castは失敗します(nullを返します)。この例では、あいまいなベースはありませんが、注意する必要があります。
  • ほとんどのクラスで仮想デストラクタを省略しましたが、それは怠惰だからです。

両方のインスタンスであるオブジェクトを繰り返し処理していることに気付いた場合は、可能であれば、それをクラス階層に反映する必要がありますAFたとえば、とGの両方から仮想的に継承する型を定義できます。次に、の代わりにから継承できます。また、を呼び出すことができるを期待するこのコードにを渡すことができます。AFDEGFG*A*Foo()

于 2013-03-06T09:09:21.753 に答える
2

クラスの正確なセマンティクスを知らずに設計アドバイスを提供することは困難です(文字は単なるシンボルであるため、これらの継承関係はOKであると想定する必要がありますが、そうではない場合もあります)。

モデルの正式な構成を見るだけで、仮想関数をに追加できます。Aこれは両方ともDオーバーライドEします。これらのオーバーライドは、実装をに委任しますF::foo()

class A { 
public:
    virtual void bar() { }; // Maybe make this pure if A is abstract
    // ...
};

// ...

class D : public C, public F { 
public:
    virtual void bar() { /* ... */ f::foo(); /* ... */ }
    // ...
};

class E : public B, public F { 
public:
    virtual void bar() { /* ... */ f::foo(); /* ... */ }
    // ...
};
于 2013-03-06T09:00:48.790 に答える
2

さまざまなクラスの役割を知らなければ、言うのは難しいですが、AFがインターフェースである場合(おそらくその場合)、A*オブジェクトがインターフェースもサポートしているかどうかを尋ねる正しい方法Fはですdynamic_cast<F*>。これにより、Fサポートされている場合はインターフェイスへのポインタが提供され、サポートされていない場合はnullポインタが提供されます。

Fそれを超えて、インターフェースがインターフェースを拡張するAかどうか、またはそれが完全に無関係であるかどうかを反映するかもしれません。それが拡張機能である場合、おそらく;Fから派生するはずです。A拡張インターフェースを実装することがわかっているオブジェクトを作成するときは、そのアドレスをに割り当て、F*将来のキャストをすべて回避します。(一般に、ポイントされたオブジェクトの一部が実装されないA*ポイントに到達するまで、に割り当てないでください。)したがって、次のようになります。F

//  interfaces...
class A {};
class F : public virtual A {};

//  implementations of A...
class C : public virtual A {};
class B : public virtual A {};

//  implementations of F (and also A, of course)
class E : public C, public virtual F {};
class D : public B, public virtual F {};

インターフェイスから派生する場合は、一般に、派生を仮想化することをお勧めします。(この場合、のすべての派生に必要ですA。ただし、同じパターンが別のレベルで繰り返される可能性があるため、のインターフェイスを拡張する新しいクラスがあるため、インターフェイスFからの派生は仮想であるというルールを採用する方が一般的に簡単です。)

Fが本当に関係ない場合はA、1つのクラスが両方を実装して何をしているのかを尋ねることもできます。Aまたは、のいくつかの(多くの?)実装も実装することが理にかなっている場合は、次のインターフェイスの一部としてへのFアクセスを提供することを検討してください。も実装するクラスは、この関数を。のようなものでオーバーライドします。FAF* A::getF() { return NULL; }FF* E::getF() { return this; }

于 2013-03-06T09:34:15.503 に答える
1

F単なる実装の詳細である場合は、@AndyProwlが言ったことを実行する必要があります。基本クラスに仮想関数を作成しますA

実装の詳細だけでF はない場合は、代わりに、処理するオブジェクトのリストをsとして保持し、処理するオブジェクトのリストをFsとして保持することもできますA。繰り返しますが、Andyが言うように、これは状況のセマンティクスに依存します。

vector<F*> effs;
vector<A*> ehs;

A* a;
F* f;
if(condition) {
    E* e = new E();
    a = e;
    f = e;
}
else {
    D* d = new D();
    a = d;
    f = d;
}

effs.push_back(f);
ehs.push_back(a);

for(A* a: ehs) {
    a->bar();
}
for(F* f: effs) {
    f->foo();
}
于 2013-03-06T09:25:29.650 に答える