2012-02-02 15 views
8

Kiedy próbuję skompilować ten kod uzyskać:C++ klasa naprzód deklaracja

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' 

jakaś część mojego kodu:

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

ja naprawdę nie wiem, co robić, szukałem rozwiązania ale nie mogłem znaleźć czegoś podobnego do mojego problemu ... Właściwie, mam więcej zajęć z rodzicem "płytka" i było ok przed ... Dziękuję za pomoc.

EDIT:

postanowiłem zmienić wszystkie typy zwracane do wskazówek, aby uniknąć wycieków pamięci, ale teraz mam:

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" 

jej tylko w klasie bazowej, wszystko jest ok ... Każdy funkcja w klasie płytek które zwracają * dachówka ma ten błąd ...

Niektóre kodu:

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

Odpowiedz

11

Aby można było skompilować new T, T musi być kompletnym typem. W twoim przypadku, gdy mówisz new tile_tree_apple w definicji tile_tree::tick, tile_tree_apple jest niekompletne (zostało zadeklarowane do przodu, ale jego definicja jest później w twoim pliku). Spróbuj przenieść wbudowane definicje funkcji do oddzielnego pliku źródłowego lub przynajmniej przenieś je po definicjach klas.

Coś jak:

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

Podczas pisania kodu w ten sposób, wszystkie odniesienia do A i B w tych metod zagwarantowane są w odniesieniu do zakończenia typów, ponieważ nie istnieją odniesienia bardziej do przodu!

+0

Czy słowo 'inline' nie musi również iść w definicji klasy? Nigdy nie jestem tego zbyt pewny ... –

+1

@KerrekSB: AFAIR, powinien iść do deklaracji lub definicji, ale nie ma znaczenia, który z nich –

+3

@KerrekSB: Musi iść dalej tylko definicji; umieszczenie go w deklaracji nie ma żadnego skutku. – ildjarn

0

Aby wykonać cokolwiek innego niż zadeklarowanie wskaźnika do obiektu, potrzebna jest pełna definicja.

Najlepszym rozwiązaniem jest przeniesienie implementacji do osobnego pliku.

Jeśli musi zachować to w nagłówku, przesuń definicję po obu deklaracji:

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

Ważne

masz wycieków pamięci:

tile tile_tree::onDestroy() {return *new tile_grass;}; 

będzie stwórz obiekt na stercie, którego nie możesz zniszczyć rwards, chyba że zrobisz jakieś brzydkie hakowanie. Ponadto twój obiekt zostanie pokrojony. Nie rób tego, zwróć wskaźnik.

+0

To nie jest prawda. Sprawdź standard. Możesz na przykład zadeklarować (ale nie zdefiniować) funkcję, która przyjmuje T, jeśli T jest niekompletnym typem. Możesz również zadeklarować odniesienia do T. –

+0

Więc czy powinienem zmienić wszystko w klasach na wskaźniki? –

+2

[This] (http://stackoverflow.com/questions/9107009/where-are-complete-types- not-required/9107130#9107130) powinno być przydatne, –

-2

Jestem noobem CPP, ale czy nie musisz określać dziedziczenia w deklaracji forward?

class tile_tree_apple : public tile;

+4

Nie, nie. . – Joe

+0

Nie, kompilator będzie potrzebował całej deklaracji ... –

5

Deklaracja naprzód jest „niekompletny typ”, jedyne co można zrobić z takiego typu jest instancję wskaźnik do niego, lub odwołać go w funkcji deklaracja (tj. i argument lub typ zwracany w prototypie funkcji). W linii 52 w kodzie próbujesz utworzyć instancję obiektu .

W tym momencie kompilator nie ma wiedzy o wielkości obiektu ani jego konstruktorze, więc nie może utworzyć instancji obiektu.

+2

Istnieje wiele innych rzeczy, które można wykonać przy użyciu niekompletnego typu. –

+1

Jak utworzyć instancję wskaźnika bez tworzenia instancji obiektu? –

+0

@Luchian: W tym przypadku: 'tile_tree_apple * tta_ptr;' Wywołuje wskaźnik typu 'tile_tree_apple *', chociaż oczywiście nie wskazuje na poprawny obiekt. Chodzi o to, że możesz mieć wskaźnik taki jak element klasy, który później tworzy instancję obiektu w konstruktorze, ale tak czy inaczej, w punkcie kodu, w którym widoczny jest typ * complete *. – Clifford

3

klasa tile_tree_apple powinna być zdefiniowana w oddzielnym pliku .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;};   
}; 

jeszcze jedno: powrót do płytki, a nie odniesienie płytka nie jest dobrym pomysłem, chyba że płytka jest bardzo prymitywny lub „małe” typ.

+0

Nie będzie tile_tree_apple trzeba wiedzieć o tile_tree chociaż include i na odwrót? – Bren

4

Problem polega na tym, że tick() potrzebuje znać definicję tile_tree_apple, ale wszystko, co ma, to deklaracja do przodu. Należy oddzielić deklaracje i definicje tak:

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

wyjątkiem masz poważny problem: jesteś przydzielania pamięci (z new) następnie kopiowanie przydzielonego obiektu i zwrócenie kopii. Nazywa się to wyciekiem pamięci, ponieważ program nie może zwolnić pamięci, której używa. Nie tylko to, ale kopiujesz tile_tree do tile, która odrzuca informacje, które powodują, że tile_tree różni się od; nazywa się to krojenie.

Co chcesz jest powrót wskaźnika do nowego tile, i upewnij się, że nazywają delete w pewnym momencie, aby zwolnić pamięć:

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

Jeszcze lepiej byłoby, aby powrócić inteligentne wskaźnik, który zajmie zarządzanie pamięcią dla ciebie:

#include <memory> 

std::shared_ptr<tile> tile_tree::tick() { 
    if (rand() % 20 == 0) 
     return std::make_shared<tile_tree_apple>(); 
} 
0

Aby wykonać *new tile_tree_apple konstruktor tile_tree_apple powinien być nazywany, ale w tym miejscu kompilator nie wie nic o tile_tree_apple, więc jest ok nie używaj konstruktora.

Jeśli umieścisz

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

w osobnym pliku cpp, który ma definicję klasy tile_tree_apple lub zawiera ten plik nagłówka, który ma wszystko, definicja będzie działać prawidłowo.

15

Użyć zgłoszenia do przodu, gdy jest to możliwe.

Załóżmy, że chcesz zdefiniować nową klasę B, która używa obiektów klasy A.

  1. B używa tylko odniesienia lub odsyłacze do A. Użyj deklaracji forward, a następnie nie musisz podawać <A.h>. To z kolei przyspieszy trochę kompilację.

    class A ; 
    
    class B 
    { 
        private: 
        A* fPtrA ; 
        public: 
        void mymethod(const& A) const ; 
    } ; 
    
  2. B wywodzi A lub B jawnie (lub implicitely) wykorzystuje obiektów klasy A. Następnie należy włączyć <A.h>

    #include <A.h> 
    
    class B : public A 
    { 
    }; 
    
    class C 
    { 
        private: 
        A fA ; 
        public: 
        void mymethod(A par) ; 
    } 
    
+0

Powinno być: void mymethod (const A &) const. – KIIV

+0

Ta odpowiedź (nr 1) zawiera błąd, więc przeszukaliśmy go. –

5

miałem to:

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

...

dostał ten sam smutek jak w pierwotnym pytaniu. Rozwiązany tylko przez przeniesienie definicji paulzSprite do po, która z spriteFrame. Czy nie powinien być mądrzejszy od kompilatora (VC++, VS 11 Beta)?

I przy okazji, zgadzam się z całą opinią Clifforda powyżej "Wskaźniki nie powodują wycieków pamięci, złe kodowanie powoduje przecieki pamięci". IMHO dotyczy to wielu innych nowych funkcji "inteligentnego kodowania", które nie powinny zastąpić zrozumienia, o co tak naprawdę prosisz komputer.

+0

Witam i zapraszam do Stack Overflow. Nie używamy tutaj podpisów, więc nie obrażaj się, jeśli/kiedy twój podpis zostanie usunięty – Jeff