2012-02-02 2 views
8

Когда я пытаюсь скомпилировать этот код я получаю:C++ опережающее объявление

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

Я действительно не знаю, что делать, я искал решение но я не мог найти ничего похожего на мою проблему ... На самом деле, у меня больше классов с родительской «плиткой», и это было нормально ... Thanx для любой помощи.

EDIT:

я решил изменить все возвращаемые типы, указатели, чтобы избежать утечек памяти, но теперь я получил:

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

ответ

11

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

+0

Не нужно ли 'inline' идти в определении класса? Я никогда не слишком уверен в этом ... –

+1

@KerrekSB: AFAIR, он должен идти либо к объявлению, либо по определению, но не имеет значения, какой из них –

+3

@KerrekSB: ему нужно только перейти к определению; включение его в декларацию не имеет никакого эффекта. – ildjarn

0

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

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

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

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

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

+0

Это неправда. Посмотрите стандарт. Например, вы можете объявить (но не определить) функцию, которая принимает T, если T является неполным типом. Вы также можете объявить ссылки на T. –

+0

Так что я должен изменить все в классах на указатели? –

+2

[This] (http://stackoverflow.com/questions/9107009/where-are-complete-types-not-required/9107130#9107130) должно быть полезно, –

-2

Я CPP noob, но вам не нужно указывать наследование в прямом объявлении?

class tile_tree_apple : public tile;

+4

Нет, нет. , – Joe

+0

Нет, тогда компилятору понадобится вся декларация ... –

5

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

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

+2

Есть еще много вещей, которые вы можете сделать с неполным типом. –

+1

Как создать экземпляр указателя без экземпляра объекта? –

+0

@Luchian: В этом случае: tile_tree_apple * tta_ptr; 'Создает указатель типа' tile_tree_apple * ', хотя, конечно, он не указывает на действительный объект. Дело в том, что у вас может быть такой указатель, что и член класса, который впоследствии создает экземпляр объекта в конструкторе, например, но в любом случае в точке кода, где вид * complete * виден. – Clifford

3

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

Не будет ли tile_tree_apple знать о tile_tree, хотя включить и наоборот? – Bren

4

Проблема в том, что 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>(); 
} 
0

для выполнения *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 или включает файл заголовка, который имеет определение все будет работать нормально.

15

При необходимости используйте форвардную декларацию.

Предположим, вы хотите определить новый класс 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) ; 
    } 
    
+0

Это должно быть: void mymethod (const A &) const. – KIIV

+0

Этот ответ (# 1) содержит ошибку, поэтому я опущен. –

5

Я имел это:

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)?

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

+0

Привет, и добро пожаловать в переполнение стека. Мы не используем подписи здесь, поэтому, пожалуйста, не обижайтесь, если/когда ваша подпись удалена – Jeff

Смежные вопросы