2017-03-11 12 views
2

だから。C++ - 基本ポインタを使って子クラスのメソッドをオーバーライドする方法

私は学校のための小さなゲームプロジェクトに取り組んでいます。私はオブジェクトに衝突して作業をしています。オブジェクトは別のオブジェクトと衝突しているかどうかを確認し、例えば、オブジェクトがプレイヤーによって制御されるオブジェクトである場合、敵はそれを傷つけるが、それが敵と衝突するパワーアップであれば、大丈夫だろう。

std::vector<Obj*> list; 
list.push_back(new Powerup()); 
list.push_back(new Enemy()); 
list.push_back(new Player()); 

for (auto i: list) { 
    for (auto j: list) { 
     if (collision(i,j)) { 
      i->doStuff(*j); 
     } 
    } 
} 

をしかし、私は適切な型に送信する方法を見つけるのトラブルを抱えている:

だから、私は(すべてが明らかにObjにから継承)私のような何かができる期待していました。

#include <iostream> 

class A { 
    public: 
     virtual void doStuff(A& T) { std::cout << "A->A!" << std::endl; } 
}; 

class B : public A { 
    public: 
     virtual void doStuff(A& T) { std::cout << "B->A!" << std::endl; } 
}; 

class C : public A { 
    public: 
     virtual void doStuff(A& T) { std::cout << "C->A!" << std::endl; } 
     virtual void doStuff(B& T) { std::cout << "C->B!" << std::endl; } 
}; 

int main() { 
    A* base; 
    A a; 
    B b; 
    C c; 

    c.doStuff(a); 
    c.doStuff(b); 

    base = &a; 
    c.doStuff(*base); 

    base = &b; 
    c.doStuff(*base); 

    return 0; 
} 

そして、それは私がこれを取得実行している:私は期待していたとき

C->A! 
C->B! 
C->A! 
C->A! 

C->A! 
C->B! 
C->A! 
C->B! 

任意のアイデアをどのようにこの作品を作るために、私はこの問題を実証するテストプログラムを作ったのですか?

答えて

0

最後の呼び出しがdoStuffになり、A&が渡されるので、C::doStuff(A&)が呼び出されます。 C::doStuff(B&)に電話する場合は、baseB&にキャストする必要があります。これはstatic_castdynamic_castで行うことができます。あなたは二重派遣をご覧ください

:と呼ばれているあなたがここにしようとしているUnderstanding double dispatch C++

+0

[OK]を、私は私のような何かをする必要があり、それを取る: 無効doStuff(A&T){ * A = dynamic_castを(T)を。 B * b = dynamic_cast (T); (A){ のstd :: COUT << "C-> A!もし "<<はstd :: ENDL; }!(B){ のstd :: COUT << "C-> Bもしそうでなければ" << std :: endl;その後、結果を得るために必要な }} ?/ –

+0

は、二重派遣に見て: はまた、私は、メインプログラム内の文であれば、その後私は、すべての新しいクラスのメインプログラムを変更する必要が...あることを行います。派生型ごとにキャストを書く必要がないようにしたい場合は、これが必要です。動的キャスティングは、BがAを派生しているかどうかわからない場合にのみ有効です。BがAを派生しない場合、 'NULL' /' nullptr'を返します。あなたがBを派生していることを知っているならば 'static_cast'を使います。さもなければあなたはUBを得るでしょう。 – Gambit

+0

ありがとう! –

3

ダブルディスパッチ - 二つの引数に関して、仮想関数を作ります。 C++は、仮想関数を持つほとんどのプログラミング言語と同様に、これをネイティブにサポートしていません。


c.doStuff(*base);コールを考えてみましょう。実際にはの2つの引数がフードの下にあります。c(これは、関数内で*thisとなります)と*baseです。

ここで問題は、doStuffが最初の引数に対してのみ仮想であることです。だからcが静的​​な基底クラス型を持っていても、CdoStuffのいずれかの関数が呼び出されることになります。しかし、2番目の引数は多態的な方法では処理されません。 baseの静的型はA*であり、サブクラスオブジェクトを指している場合でも*baseAを返し、それはA&のオーバーロードと一致します。


C++がダブルディスパッチをサポートしていないのは間違いありませんが、それをシミュレートする方法があります。ビジターデザインパターンは、それを行う方法としてよく引用されています。たとえば、Difference betwen Visitor pattern & Double Dispatchを参照してください。


P.S.:コードの人間の読者として、どの関数が呼び出されるかを知ることは非常に紛らわしくなる可能性があるので、オーバーライドとオーバーロードを混在させることはめったに良い考えではありません。

+0

はい、ついにそれを得ました、ありがとう! –

0

問題は、C++は、単一のディスパッチを使用して何がしたいことは、二重派遣であるということです。これを言うための一つの方法は、単一の発送は(タイプAのポインタはタイプBのオブジェクトを指すように起こるの代わりに、実行時に知られている情報の(あなたが関数にタイプAのポインタを渡している)コンパイル時に入手可能な情報を使用していることですコールする関数のオーバーロードを決定する際に、B)のオーバーロードを呼びたいと思っています。この上

いくつかの良いリソース:http://members.gamedev.net/sicrane/articles/dispatch.htmlhttps://en.wikipedia.org/wiki/Double_dispatch

単一のディスパッチプログラミング言語は、通常は「Visitorパターン」を使用しているをキャストすることなく、この問題、を解決するに取り掛かる方法。

class DoStuffClass{ //visitor class 
public: 
    void doStuff(B &b);//traditionally visit(B &b) 
    void doStuff(C &c); 
    void doStuff(A &a); 
}; 

class A{ //visited class 
public: 
    void interactWith(DoStuffClass &dsc){ 
     //here's the good part: 
     //Now it is known at compile time(statically) what the type of *this is, 
     //so dispatching to the desired function - doStuff(A&) - can happen 
     doc.doStuff(*this); 
    }; 
} 

class B: public A{ //visited class 
public: 
    void interactWith(DoStuffClass &dsc){ 
     //same as for A 
     doc.doStuff(*this); 
    }; 
} 

注:伝統的にビジタークラスのメンバクラスが訪問と呼ばれている(SomeVisitedClass &)とクラスのメンバ関数は受信機(ビジター&)と呼ばれて訪問しました。しかし、あなたは好きなだけでもそれを呼び出すことができます。

次に、あなたが行うことができます:

DoStuffClass dsf; 
A a; 
B b; 
A *aptr; 
aptr = &b; 
a.interactWith(dsf); //will call the do stuff function that handles A 
b.interactWith(dsf); //will call the do stuff function that handles B 
aptr->interactWith(dsf);//still calls the do stuff function that handles B 

を今、あなたはそれでいる間、あなたはまた、Mediatorパターンを見てかかることがあります。 (あなたの場合のように)動作し、他のすべてのクラスごとに認識させることからあなたを救うために、お互いを認識しておく必要があり、クラス間の依存関係を軽減するために設計されています

https://en.wikipedia.org/wiki/Mediator_pattern

+0

ああ、提出する前にページをリフレッシュしたはずです:Pクリスチャンはすでに私が言っていることの一部を述べました。それでも、最初の参照と最後の(Mediatorパターンについて)読むのは面白いかもしれません。 – Edd

+0

あなたの助けていただきありがとうございます、私はここから3つの答えを使用して解決することができました。 :) –

+0

仲間を聞くのは良いです。この答え(および/またはGambitの)が役に立つと判明した場合、Stack Overflowは答えの横に上矢印を表示して有用とマークします。それを自由にクリックしてください。 :) – Edd

関連する問題