简体   繁体   English

C++ 类前向声明

[英]C++ class forward declaration

When I try to compile this code, I get:当我尝试编译此代码时,我得到:

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' 

some part of my code:我的代码的一部分:

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

I dont really know what to do, I searched for the solution but I couldn't find anything simmilar to my problem... Actually, I have more classes with parent "tile" and It was ok before...我真的不知道该怎么做,我搜索了解决方案,但找不到任何与我的问题类似的东西......实际上,我有更多的父级“tile”类,之前还可以......

EDIT:编辑:

I decided to change all returned types to pointers to avoid memory leaks, but now I got:我决定将所有返回的类型更改为指针以避免内存泄漏,但现在我得到了:

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"

Its only in base class, everything else is ok... Every function in tile class which return *tile has this error...它仅在基类中,其他一切正常... tile 类中返回 *tile 的每个函数都有此错误...

Some code:一些代码:

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

Use forward declaration when possible.尽可能使用前向声明。

Suppose you want to define a new class B that uses objects of class A .假设您想定义一个使用类A对象的新类B

  1. B only uses references or pointers to A . B仅使用A引用或指针。 Use forward declaration then you don't need to include <Ah> .使用前向声明,则不需要包含<Ah> This will in turn speed a little bit the compilation.这将反过来加快编译速度。

     class A ; class B { private: A* fPtrA ; public: void mymethod(const& A) const ; } ;
  2. B derives from A or B explicitely (or implicitely) uses objects of class A . B派生自AB显式(或隐式)使用类A对象。 You then need to include <Ah>然后你需要包括<Ah>

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

In order for new T to compile, T must be a complete type.为了编译new TT必须是完整类型。 In your case, when you say new tile_tree_apple inside the definition of tile_tree::tick , tile_tree_apple is incomplete (it has been forward declared, but its definition is later in your file).在您的情况下,当您在tile_tree::tick的定义中说new tile_tree_apple时, tile_tree_apple是不完整的(它已被前向声明,但其定义在您的文件后面)。 Try moving the inline definitions of your functions to a separate source file, or at least move them after the class definitions.尝试将函数的内联定义移动到单独的源文件中,或者至少将它们移动到类定义之后。

Something like:就像是:

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

When you write your code this way, all references to A and B in these methods are guaranteed to refer to complete types, since there are no more forward references!当您以这种方式编写代码时,这些方法中对 A 和 B 的所有引用都保证引用完整类型,因为不再有前向引用!

The forward declaration is an " incomplete type ", the only thing you can do with such a type is instantiate a pointer to it, or reference it in a function declaration (ie and argument or return type in a function prototype).前向声明是一个“不完整类型”,你对这种类型唯一能做的就是实例化一个指向它的指针,或者在函数声明中引用它(即函数原型中的参数或返回类型)。 In line 52 in your code, you are attempting to instantiate an object .在代码的第 52 行,您试图实例化一个object

At that point the compiler has no knowledge of the object's size nor its constructor, so cannot instantiate an object.此时编译器不知道对象的大小及其构造函数,因此无法实例化对象。

I had this:我有这个:

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

... ...

Got the same grief as in the original question.得到与原始问题相同的悲伤。 Only solved by moving the definition of paulzSprite to after that of spriteFrame.只能通过将paulzSprite的定义移到spriteFrame的后面来解决。 Shouldn't the compiler be smarter than this (VC++, VS 11 Beta)?编译器不应该比这更聪明吗(VC++,VS 11 Beta)?

And btw, I wholeheartedly agree with Clifford's remark above "Pointers don't cause memory leaks, poor coding causes memory leaks".顺便说一句,我完全同意 Clifford 上面的评论“指针不会导致内存泄漏,糟糕的编码会导致内存泄漏”。 IMHO this is true of many other new "smart coding" features, which should not become a substitute for understanding what you are actually asking the computer to do.恕我直言,这适用于许多其他新的“智能编码”功能,它们不应替代理解您实际要求计算机执行的操作。

The problem is that tick() needs to know the definition of tile_tree_apple , but all it has is a forward declaration of it.问题是tick()需要知道tile_tree_apple的定义,但它所拥有的只是它的前向声明。 You should separate the declarations and definitions like so:您应该像这样分开声明和定义:

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

Except you have a major problem: you're allocating memory (with new ), then copying the allocated object and returning the copy.除非您有一个主要问题:您正在分配内存(使用new ),然后复制分配的对象并返回副本。 This is called a memory leak , because there's no way for your program to free the memory it uses.这称为内存泄漏,因为您的程序无法释放它使用的内存。 Not only that, but you're copying a tile_tree into a tile , which discards the information that makes a tile_tree different from a tile ;不仅如此,您tile_tree复制到tile ,这会丢弃使tile_treetile不同的信息; this is called slicing .这称为切片

What you want is to return a pointer to a new tile , and make sure you call delete at some point to free the memory:你想要的是返回一个指向新tile的指针,并确保你在某个时候调用delete来释放内存:

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

Even better would be to return a smart pointer that will handle the memory management for you:更好的是返回一个智能指针,它将为您处理内存管理:

#include <memory>

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

class tile_tree_apple should be defined in a separate .h file.类 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;};        
};

another thing: returning a tile and not a tile reference is not a good idea, unless a tile is a primitive or very "small" type.另一件事:返回 tile 而不是 tile 引用不是一个好主意,除非 tile 是原始类型或非常“小”的类型。

To do anything other than declare a pointer to an object, you need the full definition.除了声明指向对象的指针之外,要执行任何其他操作,您需要完整的定义。

The best solution is to move the implementation in a separate file.最好的解决方案是将实现移动到一个单独的文件中。

If you must keep this in a header, move the definition after both declarations:如果您必须将其保留在标题中,请将定义移动到两个声明之后:

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

Important重要的

You have memory leaks:你有内存泄漏:

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

will create an object on the heap, which you can't destroy afterwards, unless you do some ugly hacking.将在堆上创建一个对象,之后您无法销毁该对象,除非您进行一些丑陋的黑客攻击。 Also, your object will be sliced.此外,您的对象将被切片。 Don't do this, return a pointer.不要这样做,返回一个指针。

To perform *new tile_tree_apple the constructor of tile_tree_apple should be called, but in this place compiler knows nothing about tile_tree_apple , so it can't use the constructor.要执行*new tile_tree_apple的构造tile_tree_apple应该叫,但在这个地方编译器一无所知tile_tree_apple ,所以不能使用构造。

If you put如果你把

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

in separate cpp file which has the definition of class tile_tree_apple or includes the header file which has the definition everything will work fine.在具有类 tile_tree_apple 定义的单独 cpp 文件中或包含具有定义的头文件,一切都会正常工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM