2016-10-13 6 views
0

不完全な型とunique_ptrに関する質問があります。 ここで単純化された単純なツリー構造をしようとしていましたが、不完全な型に関するコンパイラエラーがいくつか出ていました。私が知っていたのは、作成するdtorを定義する必要があるということでした。それは問題を解決しませんでしたが、少なくとも私はそれをテストしているmsvcで。不完全な型のツリー構造とunique_ptr

私が代わりにしなければならなかったのは、不完全な型の破壊を必要とするすべてのクラスに対して、関連するヘッダーを.cppファイルに含めるだけで済むということでした。明示的に定義することは助けにはならなかったが、それは私には驚きだった。

Level1.h

#include "Level2Vector.h" 

class Level1 
{ 
public: 
    Level1() : lvl2_vec(this) {} 

private: 
    Level2Vector lvl2_vec; 
}; 

Level1.cpp

#include "Level2.h" // this was needed to not get the incomplete type 
#include "Level3.h" // this was needed to not get the incomplete type 

Level2.h

#include "Level3Vector.h" 

class Level1; 

class Level2 
{ 
public: 
    Level2(Level1* const lvl1) : parent_(lvl1), lvl3_vec(this){} 

private: 
    Level1* parent_; 
    Level3Vector lvl3_vec; 
}; 

Level2.cpp

#include "Level2.h" 
#include "Level3.h" // this was needed to not get the incomplete type 

Level3.h

class Level2; 

class Level3 
{ 
public: 
    Level3(Level2* const lvl2) : parent_(lvl2) {} 

private: 
    Level2* parent_; 
}; 

Level2Vector.h

class Level1; 
class Level2; 

class Level2Vector : public std::vector<std::unique_ptr<Level2>> 
{ 
public: 
    Level2Vector(Level1* lvl1) : parent_(lvl1) {} 

private: 
    Level1* parent_; 
}; 

Level3Vector.h

class Level2; 
class Level3; 

class Level3Vector : public std::vector<std::unique_ptr<Level3>> 

{ 
public: 
    Level3Vector(Level2* lvl2) : parent_(lvl2) {} 
    //~Level3Vector(); 

private: 
    Level2* parent_; 
}; 

私は何かが足りないのですか? Level2Vectorには、Level2.hヘッダーを含める必要があると思われるクラスがありますか?

+0

[mcve]と記入してください。 – user2079303

+0

'std :: vector'からの継承:-( – Jarod42

+0

私は独自のベクタークラスを作曲したり、私的にベクターから継承したりすることができましたが、それは問題ではなかったので、私は質問を複雑にしたくありませんでした。そして標準的なコンテナ...何か質問に関連していますか? – xerion

答えて

0

一般的に言えば、前方宣言型を使用することができます(ヘッダーファイルでは、その型を使用してポインタを定義します)。タイプの定義が必要となる前に、タイプの定義が利用可能でなければなりません。例えば。 Level1.hでは、Level2Vector型のオブジェクトを定義します(Level2Vectorへのポインタではありません)。 Level1.hにはLevel2Vector.hが含まれていなければなりません。

これを念頭に置いて、「Level2Vectorを使用している可能性のあるすべてのクラスにLevel2.hヘッダーを含める必要がありますか?答えはいいえだ。 Level2.hはLevel2クラスの定義を提供します。したがって、正しい方法は、ファイルにLevel2Vectorを使用するかどうかを指定する必要があります。ファイルにLevel2の定義が必要な場合は、Level2.hを含める必要があります。

+0

なぜコンパイルに失敗するのですか?それは、生のポインタを使用してもコンパイルできますが、インクルードは必要ありません。 unique_ptrとは別のものがあります。ヘッダーファイルは、空ではないcpp実装ファイル内にありますが、コンパイルされません。 – xerion

+0

あなたの質問が正確に取得するのは難しいです。 "Effective Modern C++"のItem 22に掲載されています。PDF版はオンラインで入手できます。要約は std :: unique_ptr pImplポインタについては、クラスヘッダー内の特別なメンバー関数を宣言しますが、実装ファイルに実装してください。デフォルトの関数実装が受け入れられても、これを行います。 上記のアドバイスはstd :: unique_ptrには適用されますが、std :: shared_ptrには適用されません。 –

+0

@DonghuiZhang:事実、 'Level2Vector'のデストラクタは' Level2'の定義を必要とし、デストラクタは 'inline'なので' Level2Vector'を使用し破棄するファイルは 'Level2'の定義を必要とします。明示的に使用しないでください。 – Jarod42

1

デストラクタは型が完全である必要がありますが、デストラクタは自動的にインラインで生成されるため、クラスが破壊される各場所には、std::unique_ptrというクラスの定義がある必要があります。

この問題を回避するには、std::unique_ptrメンバーを持つクラスごとにデストラクタを宣言するのが簡単です。だから、:

class Level3Vector 
{ 
public: 
    explicit Level3Vector(Level2* lvl2) : parent_(lvl2) {} 
    ~Level3Vector(); 

    // And so rule of 5 
    Level3Vector(const Level3Vector&) = delete; 
    Level3Vector& operator =(const Level3Vector&) = delete; 

    Level3Vector(Level3Vector&&) = default; 
    Level3Vector& operator =(Level3Vector&&) = default; 

private: 
    std::vector<std::unique_ptr<Level3>> lvl3s; 
    Level2* parent; // or std::observer_ptr<Level2> parent; 
}; 

そして、CPPに:

#include <Level3.h> 

Level3Vector::~Level3Vector() = default; 

は、その後、他のクラスでLevel3Vectorの破壊は<Level3.h>の含まを必要としません。