繁体   English   中英

删除对象时调试断言失败

[英]Debug Assertion failure when deleting object

我最近在用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);
}

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

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

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

有趣的是,这是因为您没有提供赋值运算符。 为什么需要一个? 好吧,让我们考虑一个向量。 向量对象具有一个指向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;
    }
//...
}

当您使用ObjArray.erase()对象时, std::vector<A_class>将填补产生的空白。 为此,它将后一个对象向前移动一个对象:它将分配给每个对象,直到分配了所有对象,最后,它销毁了最后一个元素,从而导致该对象的heap_space delete[] 但是请注意,由于没有复制或移动分配,因此仅分配了指针heap_space ,即,在erase()的对象中,最后一个对象处于错误状态:它持有指向已经delete[]的指针ed的bool数组。 ObjArray稍后超出范围时,所有对象将被销毁,并发生double 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. 析构函数释放原始左侧存储在临时副本中的内存。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM