【问题标题】:Error every time the destructor is called每次调用析构函数时出错
【发布时间】:2013-01-22 07:03:42
【问题描述】:

每次调用析构函数时,我都会收到一条奇怪的消息。由于我的私有变量之一是动态分配的数组 (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;
}

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

【问题讨论】:

  • 看看member的声明
  • 向我们展示整个班级。
  • 以及它是如何分配/创建的。
  • 向我们展示您如何初始化 member 以及分配它的位置。更好的是,正如@NPE 所说,向我们展示整个班级。

标签: c++ visual-c++


【解决方案1】:

您未能实现(或您未正确实现)ClassSet::ClassSet(const ClassSet&amp;)ClassSet::operator=(const ClassSet&amp;) 之一。

也就是说,你违反了Rule of Three

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

【讨论】:

  • 我认为我正确地实现了复制构造函数和 operator=。因为当我更改 aaa 中的元素时,当我分配“ClassSet aaa = b 或 ClassSet aaa;aaa=b”时,它不会更改 b 中的任何元素。
  • 如果你正确地实现了这些,那么我的理论是错误的。无论如何,我建议您不要直接使用new[],而是使用std::vector&lt;int&gt;
【解决方案2】:

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

基本上,“检测到堆损坏”仅仅意味着在给定的堆访问时,Windows 确定堆的当前状态不一致/无效。前一阵子出了点事。

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

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

【讨论】:

  • 虽然,delete 对空指针保持沉默,因此您不会发现潜在的双重删除。
  • 设置为 NULL 只是隐藏了问题,因为您不应该在开始时两次调用 delete
  • @JesusRamos:不设置 NULL 只会隐藏删除后访问成员的问题。两种方式都有争论,两种方式都隐藏了一些信息。
  • @MooingDuck 对于其他一切,还有 Valgrind:P
  • @JesusRamos 是的,在这种情况下你们是绝对正确的。更正了我的答案。
【解决方案3】:

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

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 或图书馆提供的其他容器。

【讨论】:

  • 容量是一个全局常量(数组成员可以拥有的元素数量)。将其调整为数组中已由用户填充的数字元素(不是数组启动时填充数组的随机数)
【解决方案4】:

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

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.

您可以通过不允许复制或移动指针而不是复制来缓解这种情况。

【讨论】:

  • 我没有使用默认的复制构造函数和默认的 operator = 我想我正确地实现了复制构造函数和 operator=。因为当我更改 aaa 中的元素时,当我分配“ClassSet aaa = b 或 ClassSet aaa;aaa=b”时,它不会更改 b 中的任何元素。
  • @user1988385 发布,我们可以查看。
猜你喜欢
  • 1970-01-01
  • 2010-12-29
  • 1970-01-01
  • 2020-11-25
  • 2012-08-06
  • 1970-01-01
  • 2012-07-12
  • 1970-01-01
相关资源
最近更新 更多