【问题标题】:Debug Assertion failure when deleting object删除对象时调试断言失败
【发布时间】:2013-12-02 21:04:40
【问题描述】:

我最近遇到了一些我用 C++ 编写的代码的问题,我无法找到解决方案。乍一看,这个问题似乎很简单,但由于某种原因,程序抛出了一个错误,我无法解释原因。 我不会复制粘贴我遇到错误的原始代码(因为它太麻烦了),但这是它的简化版本,它表现出完全相同的行为和相同的上下文:

#include<vector>
using namespace std;
class A_class
{
  bool *heap_space;    //can be any type of pointer
public:
  A_class()  { heap_space = new bool[4]; }
  A_class(const A_class&)  { heap_space = new bool[4]; }
  ~A_class()  { delete[] heap_space; }
};
void main()
{
  vector<A_class> ObjArr(5);
  vector<A_class>::iterator iTer = ObjArr.begin() + x; 
  //where x can be any number from 0 to 3
  ObjArr.erase(iTer);
}

我知道代码看起来非常简单,但我似乎无法弄清楚引发异常的原因。代码将抛出“调试断言失败!”每次我尝试运行它时,运行时都会显示“表达式:_BLOCK_TYPE_IS_VALID(pHead->nBlockUse)”的消息。

在容器的擦除方法期间消息不会立即出现也可能很有用。它仅在向量超出范围后出现。 因此,我一直尝试通过各种方法修复错误,方法是在向量​​超出范围之前添加代码(例如在擦除后立即重新插入新元素),但没有成功。此外,经过一些实验后,我发现该消息仅在擦除除向量的最后一个元素(ObjArr.end()-1)之外的任何内容后才会出现。如果向量的最后一个元素是被删除的元素,显然没有什么不好的事情发生。希望这些提示有所帮助。 如果有人知道为什么会发生这种情况,请向我解释。我确定我只是犯了一个菜鸟错误,因为这似乎很容易弄清楚,但我却做不到。

以上代码是在 Windows 7 下使用 Visual Studio 2013 编译的。

【问题讨论】:

  • 仅供参考,这段代码在我的电脑(x64 linux)和ideone(ideone.com/8MPbHG)上运行良好
  • 需要提供赋值运算符。

标签: c++ object vector delete-operator erase


【解决方案1】:

有趣的是,这是因为您没有提供赋值运算符。为什么需要一个?好吧,让我们考虑一个向量。矢量对象有一个指向 5 A_class 的数组的指针。它们是默认构造的,这没有问题,因为您已经定义了它。现在我们擦除:

A_class }
A_class }
A_class }-- Erase one of these
A_class }
A_class }

有趣的是,如果我们删除最后一个,我们看不到问题,只有删除索引 0 到 3 之一。为什么?好吧,当我们删除索引 2 时,我们会得到他的:

A_class
A_class
-- empty space with size = sizeof(A_class)
A_class
A_class

为了协调这个空间,在擦除结束时,std::vector 使用赋值运算符来修复数组。所以index[2] = index[3]index[3] = index[4]。现在,因为您没有声明赋值运算符,它将使用默认值,包括删除index[4]。这很糟糕,因为index[4] 会给index[3] 它的指针然后删除它,结果如下:

A_class // heap_space okay
A_class // heap_space okay
A_class // heap_space okay
A_class // heap_space deleted! will error when deconstructed

所以现在当我们退出时,我们尝试删除index[3],然后一切都崩溃了!

通过添加一个使用swap的赋值运算符,我们可以解决这个问题:

class A_class
{
public:
//...
    // note the byval argument
    A_class& operator=(A_class other) {
        std::swap(this->heap_space, other.heap_space);
        return *this;
    }
//...
}

【讨论】:

    【解决方案2】:

    当您ObjArray.erase() 一个对象时,std::vector&lt;A_class&gt; 将填补产生的空白。为此,它会将后面的对象向前移动一个对象:它分配到每个对象,直到所有对象都被分配,最后,它销毁最后一个元素,导致delete[]ing 该对象的@ 987654324@。但是请注意,由于您没有复制或移动分配,因此只分配了指针 heap_space,即在 erase() 一个对象之后,最后一个对象处于错误状态:它拥有一个指向已经 @ 987654327@ed 数组 bool。当ObjArray 之后超出范围时,所有对象都将被销毁并发生双重delete[]。这是您获得我们的调试断言的地方。

    解决问题的最简单方法是提供副本分配:

    A_class& A_class::operator= (A_class other) {
        this->swap(other);
        return *this;
    }
    void A_class::swap(A_class& other) {
        std::swap(this->heap_space, other.heap_space);
    }
    

    上述实现利用了您在所有实际需要复制分配的所有类中通常需要的三个操作:

    1. 复制构造函数在创建传递给赋值运算符的参数时创建一个新的赋值参数副本。
    2. swap() 方法将当前对象的内容与参数中的临时副本进行交换。
    3. 释放原始左侧内存的析构函数,因为它被放入临时副本中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多