Прямое объявление класса C ++ - PullRequest
12 голосов
/ 03 февраля 2012

Когда я пытаюсь скомпилировать этот код, я получаю:

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() {};
};

Ответы [ 8 ]

17 голосов
/ 07 ноября 2012

Используйте предварительную декларацию, когда это возможно.

Предположим, вы хотите определить новый класс B, который использует объекты класса A.

  1. B использует только ссылки или указатели на A.Используйте предварительное объявление, тогда вам не нужно включать <A.h>.Это, в свою очередь, немного ускорит компиляцию.

    class A ;
    
    class B 
    {
      private:
        A* fPtrA ;
      public:
        void mymethod(const& A) const ;
    } ;
    
  2. B происходит от A или B, явно или косвенно использующих объекты класса AЗатем вам нужно включить <A.h>

    #include <A.h>
    
    class B : public A 
    {
    };
    
    class C 
    {
      private:
        A fA ;
      public:
        void mymethod(A par) ;   
    }
    
15 голосов
/ 03 февраля 2012

Чтобы компилировать 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 в этих методах гарантированно ссылаются на полные типы, поскольку больше нет прямых ссылок!

8 голосов
/ 03 февраля 2012

Форвардное объявление - это " неполный тип ", единственное, что вы можете сделать с таким типом, - создать экземпляр указателя или сослаться на него в функции объявление (т. е. аргумент или тип возвращаемого значения в прототипе функции).В строке 52 в вашем коде вы пытаетесь создать экземпляр объекта .

В этот момент компилятор не знает ни размера объекта, ни его конструктора, поэтому не может создать экземпляр объекта.

6 голосов
/ 10 декабря 2012

У меня было это:

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 на после определения spriteFrame. Разве компилятор не должен быть умнее этого (VC ++, VS 11 Beta)?

И, между прочим, я полностью согласен с замечанием Клиффорда выше: «Указатели не вызывают утечек памяти, плохое кодирование вызывает утечки памяти». ИМХО, это относится ко многим другим новым функциям «умного кодирования», которые не должны заменять понимание того, что вы на самом деле просите сделать компьютер.

4 голосов
/ 03 февраля 2012

Проблема в том, что 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; это называется нарезка .

То, что вы хотите, это вернуть указатель на новый 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>();
}
3 голосов
/ 03 февраля 2012

класс 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;};        
};

Другое дело: возвращать плитку, а не ссылку на плитку, не очень хорошая идея, если только плитка не является примитивным или очень «маленьким» типом.

0 голосов
/ 03 февраля 2012

Для выполнения *new tile_tree_apple должен быть вызван конструктор tile_tree_apple, но в этом месте компилятор ничего не знает о tile_tree_apple, поэтому он не может использовать конструктор.

Если вы поставите

tile tile_tree::tick() {if (rand()%20==0) return *new tile_tree_apple;};

в отдельном файле cpp, который имеет определение класса tile_tree_apple или содержит файл заголовка, в котором есть определение, все будет работать нормально.

0 голосов
/ 03 февраля 2012

Чтобы сделать что-либо кроме объявления указателя на объект, вам нужно полное определение.

Лучшее решение - переместить реализацию в отдельный файл.

Если вы должны сохранить это в заголовке, переместите определение после обоих объявлений:

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;};

создаст объект в куче, который вы не сможете потом уничтожить, если только вы не совершите некрасивый взлом. Также ваш объект будет нарезан. Не делайте этого, верните указатель.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...