2016-06-19 2 views
0

私はC++での経験はありませんが、習得しようとしています。C++で「横向き」のポインタを型キャストするとどうなりますか

次の例では、「関連」しているクラスの階層からなる:

  • 親1両方の子であるParent2
  • Parent2はの子でありますVirtualGrandParent
  • 親2には、他のユーザー定義オブジェクト型へのポインタであるSister * m_sisterが含まれています。これは、初期化され、Parent2のコンストラクタ内に新しいキーワードを持つメモリが割り当てられます。実行中のある時点で、このポインタが0

MainClass.cppに設定されている:

#include "WorkerClass.h" 
using namespace std; 

int main() 
{ 
    WorkerClass worker; 

    worker.initialize(); 
    worker.setProperty1(); 
    worker.setProperty2(); 
// At this point m_sister (in Parent2) is set to 0x0, which makes the next method call fail (Segmentation fault). What happens? 

    worker.setAuntValue(); 

    return 0; 
} 

WorkerClass.h:

#include <map> 
#include "Child.h" 

class WorkerClass 
{ 
public: 
    void initialize() 
    { 
     Child* child1 = new Child(); 
     m_myMap[0] = child1; 
    } 

    void setProperty1() 
    { 
     VirtualGrandParent* ptr = m_myMap[0]; 
     ((Parent1*) ptr)->setProperty1(6.0); 
    } 

    void setProperty2() 
    { 
     VirtualGrandParent* ptr = m_myMap[0]; 
     ((Parent1*) ptr)->setProperty2(7.0); 
    } 

    void setAuntValue() 
    { 
     VirtualGrandParent* ptr = m_myMap[0]; 
     ((Child*) ptr)->setSisterValue(170.0); 
    } 

private: 
    map<int, VirtualGrandParent*> m_myMap; 
}; 

Child.h

#include "Parent1.h" 
#include "Parent2.h" 

class Child : public Parent1, public Parent2 
{ 
public: 
    Child(): Parent1(), Parent2() {} 
    ~Child(){}; 
}; 

親1h:

class Parent1 
{ 
public: 

    Parent1(): m_value1(0.0), m_value2(0.0) {} 
    virtual ~Parent1() {}; 

    void setProperty1(double val) {m_value1=val;} 
    void setProperty2(double val) {m_value2=val;} 

private: 
    double m_value1; 
    double m_value2; 
}; 

Parent2.h:

#include "Sister.h" 
#include "VirtualGrandParent.h" 

class Parent2 : public VirtualGrandParent 
{ 
public: 
    Parent2(): VirtualGrandParent() {m_sister = new Sister();} 
    ~Parent2(){}; 

    void setSisterValue(double val){m_sister->setValue(val);} 

protected: 
    Sister* m_sister; 
}; 

Sister.h:

class Sister { 
public: 
    Sister(): m_sisterVal(0.0) {}; 

    void setValue(double val) 
    { 
     m_sisterVal=val; 
    } 
private: 
    double m_sisterVal; 
}; 

VirtualGrandParent.h:

class VirtualGrandParent 
{ 
public: 
    VirtualGrandParent() {} 
    virtual ~VirtualGrandParent(){}; 
}; 

質問1:だから私のメインの質問は何が起こりますかVirtualGrandParentからParent1への「横向き」キャスト中に?なぜm_sisterは0ですか?メモリは上書きされますか? なぜm_sisterが0になる前に2回のメソッド呼び出しが必要ですか?

質問2:何がWorkerClass.hでポインタptrにアドレスを取った場合に起こり、その後、Parent1*ポインタ(下記の例)にキャスト?この変更でコードを実行すると、m_sisterは0に設定されません。これはちょうど偶然のことですか? (私は、ポインタへのポインタが本当にタイプParent1**でなければなりません推測?)

void setProperty1() 
{ 
    VirtualGrandParent* ptr = m_myMap[0]; 
    ((Parent1*) &ptr)->setProperty1(6.0); 
} 
+4

I質問を再定式化し、実際に問題を示す完全な例を追加することをお勧めします。私たちの誰もあなたの曖昧な記述が正確に何を意味するのかを理解するコードはありません。 – StoryTeller

+0

さて、私は今例を提供しようとしました。うまくいけば、これは理にかなっています。この例を実行すると、セグメンテーションフォルト(ヌルポインタ)が得られます。 – Hippo

+1

これは本当に問題のベスト[MCVE](http://stackoverflow.com/help/mcve)ですか? – StoryTeller

答えて

2

だから、ツリーとして親子関係を表示する場合:

VirtualGrandParent 
     \ 
     Parent2  Parent1 
      \  /
       Child 

その後、我々は親1とVirtualGrandParentがあることがわかります異なる枝にある。したがって、VirtualGrandParent* ptrParent1にキャストすると、階層を適切にキャストアップまたはダウンしていないことになります。代わりに、関連のないクラス間でキャストを終える、それを越えたキャストです。そのため、無効な結果が得られます。

継承階層に沿ってキャストする場合は、常に少なくともstatic_cast(または必要に応じてdynamic_cast)を使用してください。 static_castを指定すると、コンパイラはキャストが可能であるかどうかを検証し、それ以外の場合はエラーを返します。あなたの場合は、エラーが表示されるはずです。

適切なキャストがポインタ・ツー・クラスは、単にナンセンスであるとポインタツーポインタをキャスト... &ptrについてのあなたの2番目の質問についてはstatic_cast<Parent1*>(static_cast<Child*>(ptr))

だろう。それがまったく動作するように見える場合、それは未定義の動作の不運な運です。 (実際には正しく動作していないかもしれませんが、代わりに任意のメモリをクラスのように解釈し、somePtrの値は0以外の値になりますが、どのような場合でも有効ではありません)

+0

'static_cast (static_cast (ptr)) - > setProperty1(6.0);を使ってあなたのソリューションを試しました。 Parent2のポインタが0に設定されていません。だから、実際にはVirtualGrandParentからParent1へのキャストが可能ですが、VirtualGrandParentからChildへ、そしてChildからParent1への** 2つの**ステップで実行され、 'static_cast'を使って行う必要がありますか? – Hippo

+0

@Hippo 2つのステップ、はい。そのように考えてみましょう:継承に沿ったキャストは、一度にツリーの上下に移動するだけで、上下に移動することはできません。厳密に言うと 'static_cast'は必須ではありません。通常のCスタイルのキャストを使用できます。しかし、Cスタイルのキャストは意味を持たないものも含めて何でもできる。 'static_cast'や他のC++スタイルのキャストを使うと、間違いを防ぐのに役立ちます。 – TheUndeadFish

関連する問題