2017-08-18 8 views
0

私の質問の最も簡単な例では、次のコードスニペットで見ることができます。派生クラスへのポインタと同時にベースクラスへの参照をインスタンス化できないのはなぜですか?

ここ
class Handle : public IHandle_<Handle>{ 
    public: 
     Handle(std::unique_ptr<Derived> aDerived) 
      : derived(std::move(aDerived)), 
       base(*aDerived) {}; 

     std::unique_ptr<Derived> derived; 
     Base& base; 
}; 

、あなたはHandleクラスは、基本的にDerivedのラッパーであることがわかります。さらに重要なことは、基底クラスDerivedBaseを参照として公開したいと考えています。

class Handle : public IHandle_<Handle>{ 
    private: 
     std::unique_ptr<Derived1> myD1; 
     std::unique_ptr<Derived2> myD2; 

    public: 
     Handle(std::unique_ptr<Derived1> aD1) 
      : myD1(std::move(aD1)), 
       base1(*aD1), 
       base2(*aD1){}; 
     Handle(std::unique_ptr<Derived2> aD2) 
      : myD2(std::move(aD2)), 
       base1(*aD2), 
       base2(*aD2){}; 

     Base1& base1; 
     Base2& base2; 

}; 

私はこのように動作するようにHandleを望む理由は、私が "中「コンポーネント」としてそれを使用していますということである。この理由は、最終的に、私はこのような何かを見てHandleを望む、ということですエンティティコンポーネントシステム 'と呼ばれ、この特定のコンポーネントが同じ2つの基本クラスの2つの異なる具体的な実装からインスタンス化可能であることを希望します。私はこれを言及します。なぜなら、エンティティコンポーネントシステムのデザインパターンは、定義上、伝統的なオブジェクト指向プログラミングの慣行から逸脱しているからです。言い換えれば、私がやろうとしていることを達成するための他の方法があると知っています。私がここに挙げたもののいくつかのバリエーション。

質問

なぜ私の最初のスニペットに示す簡単なHandleの例では、失敗しませんか? Baseのメソッドにアクセスしようとすると、うまくコンパイルされますが、segフォルトがコンパイルされます。私がHandleというメンバ変数をインスタンス化する順序を変更すると、コンパイル時にいくつかのヒントが出ると思うが、何が起こっているのか分かりません。ここで

Handleの完全な実施例と、それが依存するクラスです:

#include <memory> 
#include <iostream> 

class Base{ 
    public: 
     Base(int ax) : x(ax){}; 
     virtual ~Base() = 0; 
     virtual void setVal(float a) = 0; 
     virtual float getVal() = 0 ; 

     int x; 
}; 

Base::~Base(){} 

class Derived : public Base{ 
    public: 
     Derived(int ax, int az) 
      : Base(ax), z(az){}; 

     int z; 
}; 

class Concrete : public Derived{ 
    public: 
     Concrete(int ax, int aw, int av) 
      : Derived(ax, aw), 
       v(av){}; 
     void setVal(float a) override{ 
      myVal = a; 
     } 
     float getVal() override{ 
      return myVal; 
     } 
     float myVal; 
     int v; 
}; 

class IHandle{ 
    public: 
     virtual ~IHandle() = 0; 
}; 

IHandle::~IHandle(){} 

template <class T> 
class IHandle_ : public IHandle{ 
    public: 
     virtual ~IHandle_() = 0; 
}; 

template <class T> 
IHandle_<T>::~IHandle_(){}; 

class Handle : public IHandle_<Handle>{ 
    public: 
     Handle(std::unique_ptr<Derived> aDerived) 
      : derived(std::move(aDerived)), 
       base(*aDerived) {}; 

     std::unique_ptr<Derived> derived; 
     Base& base; 
}; 


int main(){ 
    // These two pointers are owned by an EntityManager 
    std::unique_ptr<Derived> ptr(new Concrete(1, 2, 3)); 

    // we can get a reference to an IHandle from the EntityManager 
    std::unique_ptr<IHandle> aHandle(new Handle(std::move(ptr))); 

    // We need, specifically, a `Handle` implementation of `IHandle` 
    Handle& handle = static_cast<Handle&>(*aHandle); 
    // seg fault on the following line 
    handle.base.setVal(10.0); 
    std::cout << "a = " << handle.base.getVal() << std::endl; 
    return 0; 
} 

答えて

2

C++クラスのメンバーがそのように最初のスニペットを見て、あなたがそれらを宣言するために初期化され、順序ハンドルクラスのメンバーの初期化である:

  • 派生
  • ベース

それはコンストラクタでライン

derived(std::move(aDerived)) 

が適度aDerivedの状態をリセットし、derivedaDerivedの内部リソースを転送することを意味する、と述べました。だから、できるだけ早くあなたのコードは声明

base(*aDerived) 

base可能性が最も高いの呼び出し後にメモリから削除されます(基本および派生クラス内のあなたの移動のコンストラクタの実装に依存)空のオブジェクトを参照します達するとコンストラクタ自体。

したがって、コード内にあるbaseへの参照は、割り当てられていないメモリを指していると考えられ、SEG_FAULTエラーが発生します。

SEG_FAULTほとんどの場合、実行中のプロセスに割り当てられていない(まだまたはそれ以上の)メモリ領域を(あなたの場合はsetval()を参照)使用しているコードを指しています。これは役立つかもしれ

希望、 はそれはOPの問題に対する解決策が含まれている場合、この答えは改善されるだろう、良い夜 ステファノ

+0

を持っています。 – Yakk

+0

私は "なぜ"を提供すると思います。しかし、あなたのコメントに続いて、絶対に合理的です:@wesanyer、ベースクラスの参照の必要性は何ですか?なぜあなたの文脈でそれを必要としますか? –

関連する問題