次のような一般的なコードがあるとしましょう。
y.hpp:
#ifndef Y_HPP
#define Y_HPP
// LOTS OF FILES INCLUDED
template <class T>
class Y
{
public:
T z;
// LOTS OF STUFF HERE
};
#endif
ここで、作成したクラス (たとえば X) で Y を使用できるようにしたいと考えています。ただし、X のユーザーが Y ヘッダーを含める必要はありません。
そこで、クラス X を次のように定義します。
x.hpp:
#ifndef X_HPP
#define X_HPP
template <class T>
class Y;
class X
{
public:
~X();
void some_method(int blah);
private:
Y<int>* y_;
};
#endif
y_ はポインターであるため、その実装を含める必要がないことに注意してください。
実装は、個別にコンパイルされた x.cpp にあります。
x.cpp:
#include "x.hpp"
#include "y.hpp"
X::~X() { delete y_; }
void X::someMethod(int blah) { y_->z = blah; }
したがって、クライアントは「y.hpp」ヘッダーをすべて含めて処理することなく、「x.hpp」を含めるだけで X を使用できます。
main.cpp:
#include "x.hpp"
int main()
{
X x;
x.blah(42);
return 0;
}
main.cpp
これで、 とをx.cpp
別々にコンパイルできるようになり、コンパイル時main.cpp
に を含める必要がなくなりましたy.hpp
。
ただし、このコードでは生のポインターを使用する必要があり、さらに削除を使用する必要がありました。
だからここに私の質問があります:
(1) Y ヘッダーを含める必要なく、Y を X の直接メンバー (Y へのポインターではない) にする方法はありますか? (この質問に対する答えはノーだと強く思います)
(2)スマート ポインター クラスを使用して、割り当てられたヒープ Y を処理する方法はありますか? unique_ptr
当然の選択のように思えますが、行を変更するとx.hpp
から:
Y<int>* y_;
に:
std::unique_ptr< Y<int> > y_;
を含め、c++0x モードでコンパイルすると、次のエラーが表示されます。
/usr/include/c++/4.4/bits/unique_ptr.h:64: error: invalid application of ‘sizeof’ to incomplete type ‘Y<int>’
/usr/include/c++/4.4/bits/unique_ptr.h:62: error: static assertion failed: "can't delete pointer to incomplete type"
とにかく、生のポインタの代わりに標準のスマートポインタを使用し、カスタムデストラクタで生の削除を行うことでこれを行うことはありますか?
解決:
ハワード・ヒナントはそれを正しく理解しています。私たちがする必要があるのは、次のように変更することだけx.hpp
ですx.cpp
。
x.hpp:
#ifndef X_HPP
#define X_HPP
#include <memory>
template <class T>
class Y;
class X
{
public:
X(); // ADD CONSTRUCTOR FOR X();
~X();
void some_method(int blah);
private:
std::unique_ptr< Y<int> > y_;
};
#endif
x.cpp:
#include "x.hpp"
#include "y.hpp"
X::X() : y_(new Y<int>()) {} // ADD CONSTRUCTOR FOR X();
X::~X() {}
void X::someMethod(int blah) { y_->z = blah; }
そして、unique_ptr を使用することをお勧めします。ありがとうハワード!
解決策の根拠:
私が間違っている場合は修正できますが、このコードの問題は、暗黙のデフォルトコンストラクターが Y をデフォルトで初期化しようとしていて、Y について何も知らないため、それができないことです。コンストラクターを別の場所で定義すると明示的に言うことで、コンパイラーは「Y は別の場所でコンパイルされているので、Y の作成について心配する必要はありません」と考えます。
本当に、最初にコンストラクターを追加する必要がありました。コンストラクターがないと、プログラムにバグが発生します。