0

抽象型Aと2つの派生型A1とがありA2ます。
タイプAのパラメーターを受け取るクラスAにメソッドMを追加したいのですが、アドホック多相性が必要です。

確かに、私は3つの実装が必要です:A1::M(A1 a)、、、。しかし、タイプAのポインターを使用してメソッドMを呼び出す抽象的な方法が必要です。A1::M(A2 a)A2::(A1 a)A2::M(A2 a)

私はすべての署名宣言をクラスAに入れることができましたが、それはひどいです。

4

4 に答える 4

2

シミュレートされたダブルディスパッチを使用します。

class A {
public:
    virtual void M(A &) = 0;
    virtual void M(A1 &) = 0;
    virtual void M(A2 &) = 0;
};

class A1 : public A {
public:
    virtual void M(A &a) { a.M(*this); }
    virtual void M(A1 &a) { std::cout << "A1 <- A1\n"; }
    virtual void M(A2 &a) { std::cout << "A2 <- A1\n"; }
};

class A2 : public A {
public:
    virtual void M(A &a) { a.M(*this); }
    virtual void M(A1 &a) { std::cout << "A1 <- A2\n"; }
    virtual void M(A2 &a) { std::cout << "A2 <- A2\n"; }
};

(たとえば、 http: //ideone.com/nyclsを参照してください。)

基本クラスの複数のオーバーロードを回避する方法はないと思います。

于 2012-06-20T08:27:37.683 に答える
0

ポリモーフィックな動作が必要な場合、必要なのはBaseクラスAの1つのメソッドだけです。その後、そのメソッドをA1、A2に再実装できます。

その後、あなたは書くことができます:

A *a1 = new A1();
A *a2 = new A2();

a1->M(a2); //polymorphic behavior

あなたがこのようなものを作る場合:

struct A
{
    virtual void M(A *a) {}
};

struct A1 : public A
{
    virtual void M(A1 *a) {cout << "A1" << endl;}
    virtual void M(A *a) {cout << "A" << endl;}
};

それで:

A1 * a1 = new A1();
a1->M(a1); //prints "A1"
A  * a = a1;
a->M(a1); //prints "A"

私はこれがあなたが望む行動だとは思わない

于 2012-06-20T08:20:10.260 に答える
0

そんなことしてみませんか?

void A1::M( A a )
{
    if( dynamic_cast< A1* >( &a ) )
    {
        // do your A1::M1(A1 a) stuff
    }
    else
    if( dynamic_cast< A2* >( &a ) )
    {
        // do your A1::M2(A2 a) stuff
    }
    else
    {
        throw std::logic_error( "Unsupported A type." );
    }
}

そして、A2 :: Mについても同様に行いますか?

于 2012-06-20T08:25:08.183 に答える
0

これはダブルディスパッチです。あなたが書くとき:

A* p1;
A* p2;
p1->M(*p2);

*p1のタイプとのタイプの両方でディスパッチする必要があり*p2ます。

始める前に、これはさまざまな派生型n^2の関数を 意味することを理解する必要があります。nそして、どこかで、誰かが派生型のすべてを知っている必要があります(未知の型のペアに対してある種の「デフォルト」の実装を定義できる場合を除く)。

これを実装する方法は2つあります。最も単純なのは、階層が閉じている場合(つまり、クライアントコードが新しい派生クラスを導入できない場合)、基本クラスの仮想関数のホストを使用します。これらは階層の外部で呼び出されるように設計されていないため、通常は保護されています。

//  Forward references needed for all derived classes...
class A1;
class A2;
//  ...

class A
{
protectd:
    virtual void doM(A1* arg) = 0;
    virtual void doM(A2* arg) = 0;
    //  ...

public:
    virtual void M(A& arg) = 0;
};

派生クラスでは、の実装Mは常に同じです。

void A1::M(A& arg)
{
    arg.doM( this );
}

これは単純で比較的効率的ですが、新しい派生クラスを追加するたびに、抽象ベースすべての派生クラス(新しい仮想関数を実装する必要があります)を変更する必要があります。ただし、閉じた階層には便利です。さまざまな戦略がすべてソースファイルで定義され、クライアントに公開されていない、動作の一部に戦略パターンを使用するクラスで使用しました(戦略の抽象ベースは、ヘッダーで前方宣言されただけです)。 、したがって、戦略を追加した場合、ヘッダーの変更は必要ありませんでした)。

より一般的な解決策は、インデックスとしてstd::mapのペアを持つ、を 含みます。コピーできないため、直接typeid使用することはできません。typeidC++11はtype_indexそれをラップするために提供します。古いコンパイラを使用している場合は、自分でコンパイラを実装するのはかなり簡単です。基本的な原則は、(おそらくA自体で)次のようなものです。

typedef std::pair<std::type_index, std::type_index> TypePairKey;
typedef void (*FuncPtr)( M* arg1, M* arg2 );
typedef std::unordered_map<TypePairKey, FuncPtr> DispatchMap;

static DispatchMap ourDispatchMap;

と:

void M( A& arg )        //  NOT virtual !!!
{
    DispatchMap::iterator entry
        = ourDispatchMap.find(
                DispatchMap::value_type( typeid( *this ), typeid( arg ) ) );
    assert( entry != ourDispatchMap.end() );
        //  Or some default handling, maybe throw NotYetImplemented()
    (*entry->second)( this, &arg );
}

本当の問題は、個々の関数のそれぞれを記述し、それらのアドレスをマップに挿入することです(最初に使用する前に)。もちろん、関数自体は、ここからのみ呼び出され、関係するクラスのフレンドになることができる場合は、を使用できますが、まだ dynamic_castn2あります。彼ら。(よくある解決策の1つは、それらをいずれかのクラスの静的メンバーにし、各派生クラスに、担当するすべての関数の登録を行う型の静的メンバーを定義させることです。)static_cast

于 2012-06-20T08:44:56.360 に答える