次のように宣言された2つのクラスがあります。
class User
{
public:
MyMessageBox dataMsgBox;
};
class MyMessageBox
{
public:
void sendMessage(Message *msg, User *recvr);
Message receiveMessage();
vector<Message> *dataMessageList;
};
Gccを使用してコンパイルしようとすると、次のエラーが発生します。
MyMessageBoxはタイプに名前を付けません
コンパイラーがクラスUser
をコンパイルし、MyMessageBox
行に到達したとき、MyMessageBox
はまだ定義されていません。コンパイラにはMyMessageBox
が存在するという考えがないため、クラスメンバーの意味を理解できません。
MyMessageBox
が定義されていることを確認する必要がありますbeforeこれをメンバーとして使用します。これは、定義の順序を逆にすることで解決されます。ただし、循環依存関係があります。MyMessageBox
をUser
より上に移動すると、MyMessageBox
の定義では、名前User
は定義されません。
できることはforward declareUser
;つまり、宣言するが定義しないでください。コンパイル中、宣言されているが定義されていない型は、不完全型と呼ばれます。より単純な例を考えてみましょう。
struct foo; // foo is *declared* to be a struct, but that struct is not yet defined
struct bar
{
// this is okay, it's just a pointer;
// we can point to something without knowing how that something is defined
foo* fp;
// likewise, we can form a reference to it
void some_func(foo& fr);
// but this would be an error, as before, because it requires a definition
/* foo fooMember; */
};
struct foo // okay, now define foo!
{
int fooInt;
double fooDouble;
};
void bar::some_func(foo& fr)
{
// now that foo is defined, we can read that reference:
fr.fooInt = 111605;
fr.foDouble = 123.456;
}
User
を前方宣言することにより、MyMessageBox
は引き続きポインターまたは参照を形成できます。
class User; // let the compiler know such a class will be defined
class MyMessageBox
{
public:
// this is ok, no definitions needed yet for User (or Message)
void sendMessage(Message *msg, User *recvr);
Message receiveMessage();
vector<Message>* dataMessageList;
};
class User
{
public:
// also ok, since it's now defined
MyMessageBox dataMsgBox;
};
あなたはcannotこれを別の方法で行います:前述のように、クラスメンバには定義が必要です。 (理由は、コンパイラーがUser
がどのくらいのメモリーを使用するかを知る必要があり、そのメンバーのサイズを知る必要があることを知るためです。)
class MyMessageBox;
class User
{
public:
// size not available! it's an incomplete type
MyMessageBox dataMsgBox;
};
サイズがまだわからないため、機能しません。
補足として、この関数は:
void sendMessage(Message *msg, User *recvr);
おそらく、これらのいずれかをポインターでとるべきではありません。メッセージなしでメッセージを送信することも、メッセージを送信するユーザーなしでメッセージを送信することもできません。そして、これらの状況の両方は、どちらかのパラメーターに引数としてnullを渡すことで表現できます(nullは完全に有効なポインター値です!)
むしろ、参照(おそらくconst)を使用します。
void sendMessage(const Message& msg, User& recvr);
関連するメモで、次の場合:
class User; // let the compiler know such a class will be defined
class MyMessageBox
{
public:
User* myUser;
};
class User
{
public:
// also ok, since it's now defined
MyMessageBox dataMsgBox;
};
ユーザーはMyMessageBoxでポインターとして定義されているため、それも機能します
C++コンパイラは入力を1回処理します。使用する各クラスは最初に定義されている必要があります。定義する前にMyMessageBox
を使用します。この場合、2つのクラス定義を単純に交換できます。
Userの前にMyMessageBoxを定義する必要があります。これは、ユーザーがMyMessageBoxのオブジェクトをvalueでインクルードするためです(したがって、コンパイラはそのサイズを知っている必要があります)。
また、MyMessageBoxの前にforward declareUserが必要です。これは、MyMessageBoxにUser *タイプのメンバーが含まれているためです。
使用する前にプロトタイプを宣言する必要があります。
class User;
class MyMessageBox
{
public:
void sendMessage(Message *msg, User *recvr);
Message receiveMessage();
vector<Message> *dataMessageList;
};
class User
{
public:
MyMessageBox dataMsgBox;
};
編集:タイプを交換しました