2017-01-11 4 views
1

私が呼び出すメソッドが呼び出されず、代わりに他のメソッドが呼び出されるという問題があります。それは非常に奇妙で大きなデザインですが、私はその中から一つの例を取ることができました。この例はまだ大きく、私ができることはそれよりも小さいです。あなたが見ることができるようにオブジェクトがベースへのポインタをキャストした後に適切なメソッドを呼び出さない

#include <iostream> 
#include <vector> 

class IObserver 
{ 

}; 

class IAObserver : public IObserver 
{ 
public: 
    virtual void someSpecificMethod(int i) = 0; 
}; 

class ISomeOtherObject 
{ 
public: 
    virtual void someMethodFromThatObject() = 0; 
}; 

class SomeOtherObject : public ISomeOtherObject 
{ 
public: 
    virtual void someMethodFromThatObject() 
    { 
     std::cout << "Some implementation from that object" << std::endl; 
    } 
}; 

class AObserverImpl : public IAObserver, public SomeOtherObject 
{ 
public: 
    virtual void someSpecificMethod(int i) 
    { 
     std::cout << "Some implementation " << i << std::endl; 
    } 

    virtual void someMethodFromThatObject() 
    { 
     std::cout << "Better implementation from our object" << std::endl; 
    } 
}; 

class Base 
{ 
public: 
    virtual void addObserver(IObserver* observer) = 0; 
}; 

class A : public Base 
{ 
public: 
    virtual void addObserver(IObserver* observer) 
    { 
     m_observers.push_back((IAObserver*)observer); 
    } 

    void notifyObservers() 
    { 
     std::vector<IAObserver*>::iterator it; 
     for(it = m_observers.begin(); it != m_observers.end(); it++) 
     { 
      (*it)->someSpecificMethod(5); 
     } 
    } 

private: 
    std::vector<IAObserver*> m_observers; 
}; 

int main() 
{ 
    A* a = new A(); 
    SomeOtherObject* observer = new AObserverImpl(); 

    a->addObserver((IObserver*)observer); 
    a->notifyObservers(); //output: Better implementation from our object 
} 

ここIdeone http://ideone.com/yAT6M7

だだから、メソッドsomeSpecificMethodを呼び出すことになっているが、それはmehod someMethodFromThatObjectを呼びました。何が起こっているのか?このオブジェクトはスライスしていますか? CLionデバッガでは、私のsomeSpecificMethodメソッドを呼び出すことすらできません。これを回避するにはどうしたらいいですか?

+3

'(IObserver *)observer'を行いません。あなたはUBを呼び出しています – UnholySheep

+0

@ UnholySheepありがとうございました、あなたは理由とその代わりに何をすべきか説明できますか? – Winter

+0

適切なC++のキャストを使う必要があります: 'dynamic_cast' - ここであなたの例を修正しました:http://ideone.com/i3zUsC – UnholySheep

答えて

2

あなたの問題の核心はクロスキャストです。 main()インサイド

[SomeOtherObject]  [IAObserver] 
       \  /
       \  /
      [AObserverImpl] 

observerは動的型AObserverImpl*が、静的な型SomeOtherObject*を持って:あなたはこのようになります継承階層を持っています。次に、このSomeOtherObjectも含まれているオブジェクトのIAObserver部分を取得しようとします。これはクロスキャストと呼ばれ、オブジェクトの継承ツリーを介してキャストされます。

しかし、キャストを行うときに、*observerが実際にAObserverImplの一部であるという情報は、タイプシステムから失われています。だから、厳密に静的なCスタイルのキャストはreinterpret_cast(!!)に分解されます。実際にはまだSomeOtherObjectと同じ点を指しているIAObserver*を取得し、それを使用しようとすると、せいぜい厄介なバグにつながるだけです。

これは、正当な理由がなく、それを知っていない限り、ポインタにCスタイルのキャストを使用しないでください。 static_castを使用した場合、コンパイルに失敗しました。

これを解決するには、キャストを実行するために失われた型情報を何とか取り戻す必要があります。両当事者は、まだその情報を持っている:

開発

// Downcast to AObserverImpl, then upcast along the other branch 
// The upcast is superfluous, but written here for clarity 
a->addObserver(
    static_cast<IAObserver*>(
     static_cast<AObserverImpl*>(observer) 
    ) 
); 

そして... のdynamic_cast:あなたが何を考えて

// dynamic_cast uses RTTI to walk the inheritance graph at runtime. 
// It will also do error checking :) 
a->addObserver(dynamic_cast<IAObserver*>(observer)); 
+0

基本的に問題は、プログラマは、Cスタイルのコンパイラよりもエラーを投げ捨ててしまったと考えていることです。そして、このようなものは最後に噛み付く。 – PaulMcKenzie

+0

@PaulMcKenzieそれは、C++でのキャストが「かっこでCと似ている」と教えられているので、それ以上のことは分かりません。しかし、それは別の日の騒ぎです;) – Quentin

3

代わりにdynamic_castを使用する必要がある場合は、Cスタイルのキャストを使用しているという問題があります。

CスタイルのキャストはC++オブジェクトを何も知らないため、型チェックは行いません。複数継承の状況で基本クラスをキャストするには、dynamic_castを使用して、新しいポインタ用にすべてが正しく設定されていることを確認する必要があります。

サンプル内のCスタイルのキャストをすべてdynamic_castに置き換えると、正しく動作するはずです。

詳細については、C++、v-table、および複数の継承を検索してください。

+0

CスタイルのキャストはC++オブジェクトについて知っていて、 'static_cast'とポインタ調整を実行できます。あなたが混乱したときだけ、彼らは鈍い 'reinterpret_cast'に劣化します。 – Quentin

関連する問題