【问题标题】:C++ class forward declarationC++ 类前向声明
【发布时间】:2021-12-25 18:23:33
【问题描述】:

当我尝试编译这段代码时,我得到:

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"

它只在基类中,其他一切正常... tile 类中返回 *tile 的每个函数都有这个错误...

一些代码:

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

【问题讨论】:

    标签: c++ class forward-declaration


    【解决方案1】:

    除了声明一个指向对象的指针之外,你需要完整的定义。

    最好的解决方案是将实现移动到一个单独的文件中。

    如果您必须将其保留在标题中,请将定义移到两个声明之后:

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

    将在堆上创建一个对象,之后您无法销毁该对象,除非您进行了一些丑陋的黑客攻击。此外,您的对象将被切片。 不要这样做,返回一个指针。

    【讨论】:

    • 这不是真的。看标准。例如,如果 T 是不完整的类型,您可以声明(但不定义)一个接受 T 的函数。您还可以声明对 T 的引用。
    • This 应该很有用,
    • 函数的返回类型也可能不完整。
    【解决方案2】:

    为了使new T 能够编译,T 必须是一个完整的类型。在您的情况下,当您在 tile_tree::tick 的定义中说 new tile_tree_apple 时,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 的所有引用都保证引用完整类型,因为不再有前向引用!

    【讨论】:

    • inline 不是也必须进入类定义吗?我从来都不太确定这一点......
    • @KerrekSB:AFAIR,它应该去声明或定义,但不管哪个
    • @KerrekSB :只需要继续定义即可;把它放在声明上没有任何效果。
    • @kittyPL:使用smart pointers 避免内存泄漏
    • @kittyPL:指针不会导致内存泄漏,糟糕的编码会导致内存泄漏。
    【解决方案3】:

    前向声明是一个“不完整类型”,对这种类型唯一能做的就是实例化一个指向它的指针,或者在函数中引用它声明(即函数原型中的参数或返回类型)。在代码的第 52 行,您正试图实例化一个 object

    此时编译器不知道对象的大小和构造函数,因此无法实例化对象。

    【讨论】:

    • 使用不完整的类型可以做更多的事情。
    • 如何在不实例化对象的情况下实例化指针?
    • @Luchian:在这种情况下:tile_tree_apple* tta_ptr ; 实例化一个tile_tree_apple* 类型的指针,当然它并不指向一个有效的对象。关键是你可能有这样一个指针作为类的成员,例如稍后在构造函数中实例化对象,但无论哪种方式,在代码中 complete 类型可见的位置。
    • @Kerrek:也许,但也许与本次讨论无关。如果你能详细说明,它会很有用。也就是说,看看您的评论时间,我可能已经在上次编辑中涵盖了它们。
    【解决方案4】:

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

    另一件事:返回 tile 而不是 tile 引用不是一个好主意,除非 tile 是原始类型或非常“小”的类型。

    【讨论】:

    • 难道 tile_tree_apple 不需要通过 include 了解 tile_tree,反之亦然?
    【解决方案5】:

    问题是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_treetile 不同的信息;这称为切片

    您想要返回一个指向新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>();
    }
    

    【讨论】:

      【解决方案6】:

      要执行*new tile_tree_apple,应该调用tile_tree_apple的构造函数,但是在这个地方编译器对tile_tree_apple一无所知,所以它不能使用构造函数。

      如果你放

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

      在具有 tile_tree_apple 类定义或包含具有定义的头文件的单独 cpp 文件中,一切都会正常工作。

      【讨论】:

        【解决方案7】:

        尽可能使用前向声明。

        假设您要定义一个新类B,它使用类A 的对象。

        1. B 仅使用指向A 的引用或指针。使用前向声明,则不需要包含&lt;A.h&gt;。这反过来会加快编译速度。

          class A ;
          
          class B 
          {
            private:
              A* fPtrA ;
            public:
              void mymethod(const& A) const ;
          } ;
          
        2. B 派生自 AB 显式(或隐式)使用类 A 的对象。然后你需要包含&lt;A.h&gt;

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

        【讨论】:

        • 应该是:void mymethod(const A &) const.
        【解决方案8】:

        我有这个:

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

        顺便说一句,我完全同意 Clifford 上面所说的“指针不会导致内存泄漏,糟糕的编码会导致内存泄漏”。恕我直言,许多其他新的“智能编码”功能也是如此,它们不应替代理解您实际要求计算机执行的操作。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2023-03-16
          • 2011-07-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多