在析构函数中删除指针时崩溃

Crash When Deleting Pointer in Destructor

本文关键字:崩溃 指针 删除 析构函数      更新时间:2023-10-16

我遇到了对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对象中的指针似乎有问题,但我不知道它是什么。

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

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

你应该做的是:

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

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

作为一个附带说明,或者更确切地说是一个非常重要的说明,由于您的类管理资源,请考虑正确地实现复制语义和析构函数:此规则通常被称为三规则。或者使用一些智能指针,如std::shared_ptrstd::unique_ptr或任何适合您场景的指针。

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

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

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

考虑在实现new之后,在同一逻辑单元/范围内正确实现delete。。。

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