【问题标题】:Unwanted destructor call occurs with stack allocated class - C++堆栈分配的类发生不需要的析构函数调用 - C++
【发布时间】:2012-09-12 06:00:47
【问题描述】:

在 Game 类函数中,我将 Boundary 类分配给堆栈

void Game::loadContent()
{
    Boundary b(this, body); 
}

边界类有一个指向主游戏类的指针和一个指向刚体的指针。我不确定是否应该为每个使用参考?由于稍后解释的原因,此处的一些清晰度会有所帮助。

class Boundary : public DynamicEntity
{
public:
    Boundary(Game *game, btRigidBody *body);
    ~Boundary(void);

    // Override functions
    virtual void draw(float dt);
    virtual glm::vec3 getPosition();
    virtual void update(float dt);
};

DynamicEntity 类分配主体并在其析构函数中处理指针删除。

class DynamicEntity : public Entity
{
public:
    virtual ~DynamicEntity(void);

    virtual void draw(float dt) = 0;
    btRigidBody* getBody();
    glm::vec3 getPosition() = 0;
    virtual void update(float dt) = 0;

protected:
    explicit DynamicEntity(Game *game, btRigidBody *body);
    btRigidBody *m_body;
};

DynamicEntity.cpp 析构函数

DynamicEntity::~DynamicEntity(void)
{
    m_game->m_dynamicsWorld->removeRigidBody(m_body);

    delete m_body;
}

DynamicEntity 派生自所有名为 Entity 的游戏对象的基类

Entity.h

class Entity
{
public:
    // Make destructor virtual as this is a base class
    virtual ~Entity(void);

    virtual void draw(float dt) = 0;
    int getID();                                
    virtual glm::vec3 getPosition() = 0;
    virtual void update(float dt) = 0;          

protected:
    explicit Entity(Game *game);                // Abstract base constructor

    Game *m_game;
    int m_id;                                   // Unique ID
};

我不能在这个类的析构函数中对 Game 类指针调用 delete,这就是为什么我不确定作为指针传递是否是正确的方法(而不是引用)?

Entity::~Entity(void)
{
    // Derived class destructors are called first
    delete m_game;  // ERROR
}

Entity 类添加一个指向自身的指针,该指针可以通过 Game 类中的列表访问(对于在主 Game 类中迭代和调用 Entity 函数很有用)。

Entity::Entity(Game *game) 
    : m_game(game),                                         // Initialise members
    m_id(m_game->g_idGenerator->generateNewID())            // Generate unique ID                       
{
    m_game->m_entities.push_back(std::shared_ptr<Entity>(this));
}

我遇到的主要问题是,一旦 Game::loadContent() 方法完成,就会为 Entity 类调用析构函数。这会破坏存储在列表中的 *shared_ptr*,并在尝试调用任何虚拟方法时发生错误。

我希望边界指针一直存在,直到我说删除。有没有办法在不将边界分配给堆的情况下做到这一点?

编辑

响应使用const& Game的建议

看来我必须将我的实体标头更改为以下内容

Entity.h

#pragma once

#include <glm\glm\glm.hpp>

#include "Game.h"

// Forward declarations
class Game;

class Entity
{
public:
    // Make destructor virtual as this is a base class
    virtual ~Entity(void);

    // '= 0' means pure virtual function (like 'abstract' in C#)
    // This means they do not have to be declared in the source file '.cpp'
    virtual void draw(float dt) = 0;
    int getID();                                
    virtual glm::vec3 getPosition() = 0;
    virtual void update(float dt) = 0;          

protected:
    explicit Entity(const Game &game);          // Abstract base constructor

    Game m_game;
    int m_id;                                   // Unique ID
};

Game m_game 不是将 Game 类的实例分配给堆栈吗?如果它代表一个引用,它应该如何在标题中声明?

编辑 2

如果我在基实体类 const Game &amp;m_game 中存储对 Game 类的受保护引用,我似乎无法在派生类中访问 Game 类 g_wireShapeDrawer 的全局成员。

class Game
{
public:
    GL_WireShapeDrawer g_wireShapeDrawer;

    Game(void);
    ~Game(void);

    void init();
    void draw(float dt);
    void handleInput(float dt);
    void loadContent();
    void update(float dt);
};

例如,当我尝试在派生的 Boundary 类源的 draw 方法中访问全局成员时出现以下错误

void Boundary::draw(float dt)
{
    m_game.g_wireShapeDrawer.drawPlane(glm::vec3(0, 1, 0), 0.0f, glm::vec4(1, 1, 1, 1));
}

错误 C2662:“GL_WireShapeDrawer::drawPlane”:无法将“this”指针从“const GL_WireShapeDrawer”转换为“GL_WireShapeDrawer &

这是为什么?

【问题讨论】:

  • ...另一个程序员用裸指针弄糊涂了。道德:不要使用裸指针。您刚刚发现了原因。
  • 你知道不鼓励使用裸指针是件好事,但我相信程序员在摒弃它们之前应该充分理解它们。它们是语言的基本组成部分。
  • @KerrekSB:我不想和不懂这种语言的人一起工作。你会?抽象是伟大的,如果你了解它们下面发生的事情。没有抽象是无泄漏的,更好的程序员知道他们的代码实际上在做什么。
  • @Aesthete:我并不是说你不应该理解指针。你只是不应该使用它们,至少不要以这种的方式。指针当然有特定用途,但大多数人可以做更多的克制......
  • 我也不明白为什么有人会拒绝这个问题。

标签: c++ pointers heap-memory stack-memory


【解决方案1】:

永远不要从任何Entity 或派生类中删除Game 对象。它应该是应用程序关闭前最后被释放的东西之一。

您应该将它作为Game&amp; 传递给您的Entity 类。为什么?因为你只有一个Game 的实例,所以不需要重置它所指向的对象,并且它总是应该是有效的(因为游戏会在Entity 对象之前存在)。

另一种选择是在您的Game 类中实现单例模式,并像这样访问它Game::GetInstance().m_dynamicsWorld-&gt;removeRigidBody(m_body);

根据您的编辑,您可以使用初始化列表创建一个Entity。这样您就可以存储 const 成员,如下所示:

class Entity
{
  protected:
    explicit Entity(Game &game) : m_game(game) {}
  private:
    Game& m_game;
}

【讨论】:

  • 当作为 const &Game 传递时,我必须将类作为指针传递吗? 边界(*this, body);
  • @user1423893: *this 不是指针; this 是一个指针。但是,是的,你会使用*this
  • 这就是你传递它的方式,是的,但你实际上是在取消 this 指针并通过引用传递游戏对象。
  • * 通常表示一个指针。在这种情况下为什么不呢?是否有解释原因的教程?
  • * 在你声明一个变量或参数时表示一个指针,在这种情况下它是取消引用,或者说“带我到这个指针指向的对象”
【解决方案2】:

你的设计有缺陷。您需要清楚地说明(通过您的设计)谁拥有指针。如果Entity 拥有该指针,那么它应该像您所做的那样在其析构函数中释放它(更好;只需将其包装在std::unique_ptr 中)。如果它不拥有指针,那么就不要释放它。

你不能同时拥有它。您正在使用shared_ptr,这意味着多个“所有者”,一旦最后一个所有者完成了它,内存将被释放。同样,您需要围绕谁拥有此内存进行明确设计。

从您的代码来看,Entity 似乎并不真正拥有Game*。它出于实现原因需要它,但不应对其释放负责。


顺便说一句,您违反了The Rule of Three

【讨论】:

  • 你需要清楚地说明(通过你的设计)谁拥有指针我很难理解这一点。实体拥有自己,但指针存储在指向实体类的指针的 std::list 中。那么,当我完成它时,我可以在哪里调用 Entity 的析构函数呢?分配给堆栈仍然会导致Boundary(*this, body); 出现问题,并且在不需要的时间调用了析构函数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-24
  • 2010-11-24
  • 1970-01-01
  • 2013-04-14
  • 2011-07-29
  • 2016-04-14
相关资源
最近更新 更多