【问题标题】:Crash When Deleting Pointer in Destructor在析构函数中删除指针时崩溃
【发布时间】:2012-03-13 03:18:07
【问题描述】:

我遇到了我对 C++ 中指针缺乏深入理解的问题。我编写了一个名为 Skymap 的类,其定义如下:

class Skymap {
 public:
  Skymap();
  ~Skymap();
  void DrawAitoffSkymap();
 private:
  TCanvas mCanvas;

  TBox* mSkymapBorderBox;
};

函数定义为:

#include "Skymap.h"

Skymap::Skymap()
{
  mCanvas.SetCanvasSize(1200,800);
  mMarkerType=1;
}

Skymap::~Skymap()
{
  delete mSkymapBorderBox;
}

void Skymap::DrawAitoffSkymap()
{
  TBox* mSkymapBorderBox=new TBox(-200,-100,200,100);
  //Use the mSkymapBorderBox pointer for a while
}

(我使用的是ROOT绘图包,但我认为这只是一个一般的C++问题)。

现在,以下程序将在到达 skymap2 的析构函数时崩溃:

int main(){
  Skymap skymap1;
  Skymap skymap2;
  skymap1.DrawAitoffSkymap();
  skymap2.DrawAitoffSkymap();
  return(0);
}

但是,以下内容不会崩溃:

int main(){
  Skymap skymap1;
  skymap1.DrawAitoffSkymap();
  return(0);
}

此外,如果我在构造函数中将指针 mSkymapBorderBox 初始化为 NULL,则在执行第一个程序(带有 2 个 Skymap 对象)后我不再遇到崩溃。

谁能解释一下造成这种情况的根本原因是什么?第二个 Skymap 对象中的指针似乎有问题,但我看不到它是什么。

【问题讨论】:

  • 这不是导致此问题的原因,但在管理资源时请始终记住Rule of Three。更好的是,不要自己管理资源 - 使用 smart pointers、容器和其他 RAII 类。
  • 好吧,如果当时没有在构造函数中分配,你应该总是将指针设置为NULL

标签: c++ pointers destructor


【解决方案1】:
TBox* mSkymapBorderBox=new TBox(-200,-100,200,100);

在这里,您将内存分配给局部变量,而不是成员变量。而且由于您没有为成员变量分配内存,因此对其调用 delete 会调用未定义的行为,这会导致您的情况崩溃。

你应该做的是:

mSkymapBorderBox=new TBox(-200,-100,200,100);

现在为成员变量分配内存。这是局部变量的命名方式应不同于成员变量的原因之一。命名约定有助于避免此类错误。

作为旁注,或者说一个非常重要的说明,由于您的类管理资源,因此请考虑正确实现复制语义和析构函数:此规则通常称为the-rule-of-three。或者使用一些智能指针,例如std::shared_ptrstd::unique_ptr 或任何适合您场景的指针。

【讨论】:

  • 哇哦,非常感谢!有点尴尬,但我感谢您的快速回复!
  • 即使是最有经验的开发人员也会犯这样的错误。提问永远不会令人尴尬;-)欢迎您
  • 但是如果你不检查mSkymapBorderBox是否已经被分配(加上初始化到NULL),再调用DrawAitoffSkymap会导致内存泄漏。
【解决方案2】:

纳瓦兹的回答是正确的。但除此之外,您的代码还有几个可能的问题:

  1. 如果有人创建了 SkyMap 并且从不使用它调用 DrawAitoffSkymap,那么您将获得未定义的行为(因为 mSkymapBorderBox 从未初始化,它将有一个随机值,然后您将其删除)。
  2. 如果有人使用给定的 SkyMap 多次调用 DrawAitoffSkymap,那么您将遇到内存泄漏。

修复:

(1) 在构造函数中将 mSkymapBorderBox 初始化为零。

(2) 决定 DrawAitoffSkymap 被多次调用时应该做什么。如果它应该重用旧的 mSkymapBorderBox,那么你会想说:

void Skymap::DrawAitoffSkymap() {
   if (!mSkymapBorderBox) mSkymapBorderBox = new TBox(...);
   ...
}

另一方面,如果每次都要创建一个新的 TBox,那么你想要:

void Skymap::DrawAitoffSkymap() {
   delete mSkymapBorderBox; // note: does nothing if mSkymapBorderBox == 0
   mSkymapBorderBox = new TBox(...);
   ...
}

【讨论】:

  • 非常感谢 - 我以前没有处理过那么多指针,所以我需要养成在我的代码中添加此类检查等的习惯。我会记住你的建议!
【解决方案3】:

TBox* mSkymapBorderBox=new TBox(-200,-100,200,100); 你正在创建一个新的TBox* 指针,它不是数据成员。

在实现new 后,考虑在相同的逻辑单元/范围内正确实现delete...

【讨论】:

    【解决方案4】:
    TBox* mSkymapBorderBox=new TBox(-200,-100,200,100);
    

    当你声明这个时,它会创建一个 TBox 类的对象。当你退出 当时的 DrawAitoffSkymap 这失去了对这块分配内存的引用。

    当析构函数被调用时,它会释放一些垃圾内存。

    为避免这种情况,请使用此
    mSkymapBorderBox=new TBox(-200,-100,200,100);
    而不是TBox* mSkymapBorderBox=new TBox(-200,-100,200,100);

    【讨论】:

      猜你喜欢
      • 2018-01-03
      • 1970-01-01
      • 2018-11-01
      • 2023-03-15
      • 2021-10-12
      • 2014-12-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多