このコードをコンパイルしようとすると、次のようになります:
52 C:\Dev-Cpp\Projektyyy\strategy\Tiles.h invalid use of undefined type `struct tile_tree_Apple'
46 C:\Dev-Cpp\Projektyyy\strategy\Tiles.h forward declaration of `struct tile_tree_Apple'
私のコードの一部:
class tile_tree_Apple;
class tile_tree : public tile
{
public:
tile onDestroy() {return *new tile_grass;};
tile tick() {if (Rand()%20==0) return *new tile_tree_Apple;};
void onCreate() {health=Rand()%5+4; type=TILET_TREE;};
};
class tile_tree_Apple : public tile
{
public:
tile onDestroy() {return *new tile_grass;};
tile tick() {if (Rand()%20==0) return *new tile_tree;};
void onCreate() {health=Rand()%5+4; type=TILET_TREE_Apple;};
tile onUse() {return *new tile_tree;};
};
私は本当に何をすべきかわかりませんが、解決策を探しましたが、私の問題に似たものを見つけることができませんでした...実際、親「タイル」でより多くのクラスがあり、以前は大丈夫でした...助けてくれてありがとう.
編集:
メモリリークを回避するために、返されるすべての型をポインタに変更することにしましたが、次のようになりました。
27 C:\Dev-Cpp\Projektyyy\strategy\Tiles.h ISO C++ forbids declaration of `tile' with no type
27 C:\Dev-Cpp\Projektyyy\strategy\Tiles.h expected `;' before "tick"
その基本クラスでのみ、他のすべては大丈夫です... *タイルを返すタイルクラスのすべての関数にこのエラーがあります...
いくつかのコード:
class tile
{
public:
double health;
tile_type type;
*tile takeDamage(int ammount) {return this;};
*tile onDestroy() {return this;};
*tile onUse() {return this;};
*tile tick() {return this};
virtual void onCreate() {};
};
new T
コンパイルするには、T
は完全な型でなければなりません。あなたの場合、new tile_tree_Apple
の定義内tile_tree::tick
、tile_tree_Apple
は不完全です(前方宣言されていますが、その定義は後でファイルにあります)。関数のインライン定義を別のソースファイルに移動するか、少なくともクラス定義の後に移動してみてください。
何かのようなもの:
class A
{
void f1();
void f2();
};
class B
{
void f3();
void f4();
};
inline void A::f1() {...}
inline void A::f2() {...}
inline void B::f3() {...}
inline void B::f4() {...}
この方法でコードを記述すると、これらのメソッドでのAおよびBへのすべての参照は、前方参照がなくなるため、完全な型を参照することが保証されます!
可能な場合は、前方宣言を使用します。
クラスB
のオブジェクトを使用する新しいクラスA
を定義するとします。
B
は、A
への参照またはポインターのみを使用します。前方宣言を使用すると、<A.h>
を含める必要はありません。これにより、コンパイルが少し速くなります。
class A ;
class B
{
private:
A* fPtrA ;
public:
void mymethod(const& A) const ;
} ;
B
はA
またはB
から派生し、明示的に(または暗黙的に)クラスA
のオブジェクトを使用します。次に、<A.h>
を含める必要があります
#include <A.h>
class B : public A
{
};
class C
{
private:
A fA ;
public:
void mymethod(A par) ;
}
前方宣言は「不完全な型」です。そのような型でできることは、それに対してpointerをインスタンス化するか、関数で参照することです宣言(および関数プロトタイプの引数または戻り値の型)。コードの52行目で、objectをインスタンス化しようとしています。
その時点では、コンパイラーはオブジェクトのサイズもコンストラクターも認識していないため、オブジェクトをインスタンス化できません。
私はこれを持っていました:
class paulzSprite;
...
struct spriteFrame
{
spriteFrame(int, int, paulzSprite*, int, int);
paulzSprite* pSprite; //points to the Sprite class this struct frames
static paulzSprite* pErase; //pointer to blanking Sprite
int x, y;
int Xmin, Xmax, Ymin, Ymax; //limits, leave these to individual child classes, according to bitmap size
bool move(int, int);
bool DrawAt(int, int);
bool dead;
};
spriteFrame::spriteFrame(int initx, int inity, paulzSprite* pSpr, int winWidth, int winHeight)
{
x = initx;
y= inity;
pSprite = pSpr;
Xmin = Ymin = 0;
Xmax = winWidth - pSpr->width;
Ymax = winHeight - pSpr->height;
dead = false;
}
...
元の質問と同じ悲嘆を覚えました。 paulzSpriteの定義をafter spriteFrameの定義に移動することでのみ解決します。コンパイラはこれよりも賢くはないはずです(VC++、VS 11 Beta)。
ところで、「ポインターはメモリリークを引き起こさない、コーディングの悪さはメモリリークを引き起こす」という上記のCliffordの発言に心から同意します。私見これは、他の多くの新しい「スマートコーディング」機能にも当てはまります。これらの機能は、コンピューターに実際に何を求めているのかを理解するための代替となるべきではありません。
問題は、tick()
がtile_tree_Apple
の定義を知る必要があるが、それが持っているのはそれの前方宣言だけだということです。次のように宣言と定義を分離する必要があります。
tile_tree.h
#ifndef TILE_TREE_H
#define TILE_TREE_H
#include "tile.h"
class tile_tree : public tile
{
public:
tile onDestroy();
tile tick();
void onCreate();
};
#endif
tile_tree.cpp
:
tile tile_tree::onDestroy() {
return *new tile_grass;
}
tile tile_tree::tick() {
if (Rand() % 20 == 0)
return *new tile_tree_Apple;
}
void tile_tree::onCreate() {
health = Rand() % 5 + 4;
type = TILET_TREE;
}
例外重大な問題があります:メモリを割り当て(new
で)、割り当てられたオブジェクトをコピーして、コピーを返します。これはメモリリークと呼ばれます。プログラムが使用するメモリを解放する方法がないためです。それだけでなく、tile_tree
をtile
にコピーします。これにより、tile_tree
とtile
が異なる情報が破棄されます。これはsliceingと呼ばれます。
必要なのは、新しいtile
へのポインターを返し、ある時点でdelete
を呼び出してメモリを解放することです。
tile* tile_tree::tick() {
if (Rand() % 20 == 0)
return new tile_tree_Apple;
}
さらに良いのは、メモリ管理を処理するスマートポインタを返すことです。
#include <memory>
std::shared_ptr<tile> tile_tree::tick() {
if (Rand() % 20 == 0)
return std::make_shared<tile_tree_Apple>();
}
クラスtile_tree_Appleは、別個の.hファイルで定義する必要があります。
tta.h:
#include "tile.h"
class tile_tree_Apple : public tile
{
public:
tile onDestroy() {return *new tile_grass;};
tile tick() {if (Rand()%20==0) return *new tile_tree;};
void onCreate() {health=Rand()%5+4; type=TILET_TREE_Apple;};
tile onUse() {return *new tile_tree;};
};
file tt.h
#include "tile.h"
class tile_tree : public tile
{
public:
tile onDestroy() {return *new tile_grass;};
tile tick() {if (Rand()%20==0) return *new tile_tree_Apple;};
void onCreate() {health=Rand()%5+4; type=TILET_TREE;};
};
別のこと:タイルがプリミティブまたは非常に「小さな」タイプでない限り、タイル参照ではなくタイルを返すことは良い考えではありません。
オブジェクトへのポインタを宣言する以外のことを行うには、完全な定義が必要です。
最良の解決策は、実装を別のファイルに移動することです。
mustこれをヘッダーに保持する場合、両方の宣言の後に定義を移動します。
class tile_tree_Apple;
class tile_tree : public tile
{
public:
tile onDestroy();
tile tick();
void onCreate();
};
class tile_tree_Apple : public tile
{
public:
tile onDestroy();
tile tick();
void onCreate();
tile onUse();
};
tile tile_tree::onDestroy() {return *new tile_grass;};
tile tile_tree::tick() {if (Rand()%20==0) return *new tile_tree_Apple;};
void tile_tree::onCreate() {health=Rand()%5+4; type=TILET_TREE;};
tile tile_tree_Apple::onDestroy() {return *new tile_grass;};
tile tile_tree_Apple::tick() {if (Rand()%20==0) return *new tile_tree;};
void tile_tree_Apple::onCreate() {health=Rand()%5+4; type=TILET_TREE_Apple;};
tile tile_tree_Apple::onUse() {return *new tile_tree;};
重要
メモリリークがあります。
tile tile_tree::onDestroy() {return *new tile_grass;};
ヒープ上にオブジェクトを作成しますが、いハッキングを行わない限り、後で破壊することはできません。また、オブジェクトがスライスされます。 これをしないで、ポインタを返します。
*new tile_tree_Apple
を実行するには、tile_tree_Apple
のコンストラクターを呼び出す必要がありますが、この場所ではコンパイラーはtile_tree_Apple
について何も知らないため、コンストラクターを使用できません。
入れたら
tile tile_tree::tick() {if (Rand()%20==0) return *new tile_tree_Apple;};
クラスtile_tree_Appleの定義を持つ別のcppファイル、または定義を持つヘッダーファイルを含むすべてが正常に機能します。