2012-02-02 21 views
8

मुझे इस कोड को संकलित करने की कोशिश मैं मिलता है:सी ++ वर्ग आगे घोषणा

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।

संपादित करें:

मैं संकेत करने के लिए सभी लौट आए प्रकार बदलने के लिए मेमोरी लीक से बचने के लिए फैसला किया है, लेकिन अब मुझे मिल गया:

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 एक पूर्ण प्रकार होना चाहिए। आपके मामले में, जब आप 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() {...} 

जब आप अपने कोड इस तरह से लिखते हैं, इन तरीकों में एक के सभी संदर्भ और बी प्रकार पूरा करने के लिए संदर्भित करने के लिए, के बाद से वहाँ कोई और अधिक आगे संदर्भ हैं गारंटी है!

+0

क्या 'इनलाइन' को कक्षा परिभाषा में भी जाना नहीं है? मैं इस बारे में भी यकीन है कि कभी नहीं हूँ ... –

+1

@KerrekSB: AFAIR, यह घोषणा या परिभाषा के लिए या तो जाना चाहिए, लेकिन कोई फर्क नहीं पड़ता जो एक –

+3

@KerrekSB: यह केवल परिभाषा पर जाने की जरूरत है; इसे घोषणा पर डालने से कोई प्रभाव नहीं पड़ता है। – ildjarn

0

किसी ऑब्जेक्ट को पॉइंटर घोषित करने के अलावा कुछ भी करने के लिए, आपको पूर्ण परिभाषा की आवश्यकता है।

सर्वोत्तम समाधान एक अलग फ़ाइल में कार्यान्वयन को स्थानांतरित करना सबसे अच्छा समाधान है।

आप एक हैडर में रखना चाहिए, दोनों घोषणाओं के बाद परिभाषा के लिए कदम:

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

करेंगे:

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

महत्वपूर्ण

आप मेमोरी लीक है ढेर पर एक वस्तु बनाएँ, जिसे आप afte को नष्ट नहीं कर सकते हैं पुरस्कार, जब तक आप कुछ बदसूरत हैकिंग नहीं करते हैं। इसके अलावा, आपकी वस्तु काटा जाएगा। ऐसा मत करो, एक सूचक वापस करें।

+0

यह सच नहीं है। मानक देखो। उदाहरण के लिए, आप एक ऐसा फ़ंक्शन घोषित कर सकते हैं (लेकिन परिभाषित नहीं) एक टी जो टी टी को अधूरा प्रकार लेता है। आप टी –

+0

के संदर्भ भी घोषित कर सकते हैं तो क्या मुझे कक्षाओं में पॉइंटर्स में सबकुछ बदलना चाहिए? –

+2

[यह] (http://stackoverflow.com/questions/9107009/where-are-complete-types-not-required/9107130#9107130) उपयोगी होना चाहिए, –

-2

मैं एक सीपीपी नोब हूं, लेकिन आपको आगे की घोषणा में विरासत निर्दिष्ट नहीं करना है?

class tile_tree_apple : public tile;

+4

नहीं आप नहीं करते हैं। । – Joe

+0

नहीं है, तो संकलक पूरे घोषणा की आवश्यकता होगी ... –

5

आगे घोषणा एक "अधूरा प्रकार", केवल एक चीज आप इस तरह के एक प्रकार के साथ क्या कर सकते हैं यह करने के लिए एक सूचक का दृष्टांत, या एक समारोह में इसे संदर्भ घोषणा (है यानी एक फ़ंक्शन प्रोटोटाइप में तर्क या वापसी प्रकार)। आपके कोड में लाइन 52 में, आप ऑब्जेक्ट को तुरंत चालू करने का प्रयास कर रहे हैं।

उस समय संकलक को ऑब्जेक्ट के आकार और न ही इसके कन्स्ट्रक्टर का कोई ज्ञान नहीं है, इसलिए किसी ऑब्जेक्ट को तुरंत चालू नहीं किया जा सकता है।

+2

ऐसे कई और बातें आप एक अधूरी प्रकार के साथ कर सकते हैं। –

+1

किसी ऑब्जेक्ट को तत्काल किए बिना पॉइंटर को कैसे चालू करता है? –

+0

@ ल्यूचियन: इस मामले में: 'tile_tree_apple * tta_ptr;' प्रकार के सूचक 'tile_tree_apple * 'को तत्काल करता है, हालांकि निश्चित रूप से यह मान्य ऑब्जेक्ट को इंगित नहीं करता है। मुद्दा यह है कि आपके पास ऐसे वर्ग के सदस्य के रूप में ऐसा पॉइंटर हो सकता है जो बाद में ऑब्जेक्ट को कन्स्ट्रक्टर में उदाहरण के लिए तत्काल करता है, लेकिन किसी भी तरह से, कोड में बिंदु पर जहां * पूरा * प्रकार दिखाई देता है। – 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* tile_tree::tick() { 
    if (rand() % 20 == 0) 
     return new tile_tree_apple; 
} 

और भी बेहतर एक स्मार्ट सूचक है कि संभाल लेंगे लौटने के लिए होगा:

तुम क्या चाहते एक नया tile के सूचक के लौटने के लिए, और सुनिश्चित करें कि आप स्मृति को मुक्त करने के कुछ बिंदु पर delete फोन बनाने के लिए है आप के लिए स्मृति प्रबंधन:

#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 जानता है, तो यह ca कन्स्ट्रक्टर का उपयोग नहीं करें।

यदि आप जो वर्ग tile_tree_apple की परिभाषा है या हेडर फाइल जो परिभाषा सब कुछ ठीक काम करेंगे है शामिल हैं अलग cpp फ़ाइल में

tile tile_tree::tick() {if (rand()%20==0) return *new 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 पर स्पष्ट रूप (या implicitely) वर्ग A की वस्तुओं का उपयोग करता है से। इसके बाद आप <A.h>

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

यह होना चाहिए: शून्य mymethod (कॉन्स ए और) कॉन्स। – 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 की है कि द्वारा हल किया। क्या संकलक इस से अधिक स्मार्ट नहीं होना चाहिए (वीसी ++, वीएस 11 बीटा)?

और btw, मैं तहे दिल से क्लिफर्ड की टिप्पणी से सहमत ऊपर "प्वाइंटर मेमोरी लीक कारण नहीं है, गरीब कोडिंग मेमोरी लीक का कारण बनता है"। आईएमएचओ यह कई अन्य नई "स्मार्ट कोडिंग" सुविधाओं के बारे में सच है, जो समझने के लिए एक विकल्प नहीं बनना चाहिए कि आप वास्तव में कंप्यूटर से क्या पूछ रहे हैं।

+0

हाय, और स्टैक ओवरफ़्लो में आपका स्वागत है। हम यहां हस्ताक्षर का उपयोग नहीं करते हैं, इसलिए अगर आपका हस्ताक्षर हटा दिया गया है तो कृपया नाराज न हों – Jeff

संबंधित मुद्दे