每次调用析构函数时都会出错

时间:2013-01-21 20:54:33

标签: c++ visual-c++

每次调用析构函数时,我都会收到一条奇怪的消息。由于我的一个私有变量是动态分配数组(int *member;),我写这样的析构函数:

ClassSet::~ClassSet(){delete []member;}

每次调用ClassSet的析构函数时,都会收到一条错误消息:

  

Windows在Hw1.exe中触发了断点。

     

这可能是由于堆损坏,这表示Hw1.exe或其加载的任何DLL中存在错误。

     

这也可能是由于用户在Hw1.exe有焦点时按下F12。

全班:

class ClassSet
{
  public:
    ClassSet(int n = DEFAULT_MAX_ITEMS);
ClassSet(const ClassSet& other);
ClassSet &operator=(const ClassSet& other);
~ClassSet();
  private:
    int size;
int *member;
 };

ClassSet::ClassSet(int n){
   size = n;
   member = new int[n];
}

ClassSet::ClassSet(const ClassSet& other){
    int i = 0;
    this->size = other.size;
member = new int [capacity];
while (i<size)
{
    this->member[i] = other.member[i];
    i++;
}
 }

 Multiset& Multiset::operator=(const Multiset &other)
 {
    if (&other == this){return *this;}
this->size = other.size;
int i = 0;
    delete [] member;
    member = new int[size];
while (i<other.size)
{
    this->member[i] = other.member[i];
    i++;
}
return *this;
}

知道这个析构函数有什么问题吗?

4 个答案:

答案 0 :(得分:4)

您未能在ClassSet::ClassSet(const ClassSet&)ClassSet::operator=(const ClassSet&)之一实施(或您实施不当)。

换句话说,您违反了Rule of Three

然而,最好的解决方案可能来实现它们,而是改变为动态数组分配空间的方式。不要使用new[]delete[],而是尝试使用std::vector<>替换该成员。

答案 1 :(得分:3)

堆腐败通常是事后检测到的。它可能与您的析构函数有关,或者正如我所见,可能会在堆访问错误发生之前发生。

基本上“检测到堆损坏”只是意味着在给定的堆访问中,Windows确定堆的当前状态不一致/无效。一段时间之前发生了变化。

这些错误很难追查。堆损坏的一个常见原因是双重删除您无意中删除了两次。这可以指出围绕代码和设计复制数据的更深层次问题。

正如其他人所说,当没有适当的复制构造函数/赋值运算符来复制动态内存时,就会发生这种情况。 “复制”删除你的内存,然后初始类再次删除,导致双重删除。

答案 2 :(得分:1)

如果您已经发布了实际代码,那么我认为问题在于:

ClassSet::ClassSet(const ClassSet& other){
    int i = 0;
    this->size = other.size;
    member = new int [capacity];  // <--- what is capacity?
    while (i<size)
    {
        this->member[i] = other.member[i];
        i++;
    }
}

您正在根据名为capacity的内容调整复制的数组,该内容与other.size没有任何明显的关系。如果capacity小于size,则复制元素的循环将破坏堆。

假设这是一个学术练习,一旦你解决了这个问题,你应该研究用于这类课程的copy/swap idiom,以确保异常安全。

如果这不是学术练习,那么您应该查看std::vector或图书馆中提供的其他容器。

答案 3 :(得分:0)

这个问题很常见。默认的复制构造函数等同于

ClassSet(const ClassSet& other) {
    size = other.size;
    member = other.member;
}

这个问题是,当复制实例ClassSet时,原始实例和新实例都会保存一个指向member的原始指针。两个析构函数都将释放member,导致您看到的双重自由问题。

例如,

{
    ClassSet a
    ClassSet b(a); // assert(b.member == a.member)
} // At this point, both a and b will free the same pointer.

您可以通过不允许复制或移动指针而不是复制来缓解此问题。