简体   繁体   English

删除对象时调试断言失败

[英]Debug Assertion failure when deleting object

I've recently encountered a problem with some code I wrote in C++ which I couldn't figure out a solution for. 我最近在用C ++编写的某些代码中遇到了一个问题,但找不到解决方案。 The problem seemed very simple at first glance but for some reason the program throws an error and I cannot explain why. 乍一看,这个问题似乎很简单,但是由于某种原因,程序抛出错误,我无法解释原因。 I'm not gonna copy-paste the original code in which I encountered the error (since it's way too cumbersome) but here's a simplified version of it which exhibits the exact same behavior and in the same context: 我不会复制-粘贴遇到错误的原始代码(因为它太麻烦了),但这是它的简化版本,在相同的上下文中表现出完全相同的行为:

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

I know the code looks unrealistically simple but I just can't seem to figure out the cause of the thrown exception. 我知道代码看起来非常简单,但是我似乎无法找出引发异常的原因。 The code will throw a "Debug Assertion Failed!" 该代码将引发“调试断言失败!” message during runtime with "Expression: _BLOCK_TYPE_IS_VALID(pHead->nBlockUse)" every time I try to run it. 每次我尝试运行它时,都会在运行时显示“ Expression:_BLOCK_TYPE_IS_VALID(pHead-> nBlockUse)”消息。

It might also be useful to mention that the message does not appear immediately during the erase method of the container. 提及该消息在容器的擦除方法期间不会立即出现可能也很有用。 It appears only after the vector goes out of scope. 仅在向量超出范围后才显示。 As such I kept trying to repair the bug through various methods by adding code before the vector gets out of scope (like reinserting a new element immediately after erasing) but with no success. 因此,我一直试图通过各种方法来修复错误,方法是在向量​​超出范围之前添加代码(例如在擦除后立即重新插入新元素),但没有成功。 Also, after experimenting a little I found out that the message only appears after erasing anything except for the last element of the vector (ObjArr.end()-1). 另外,在进行了一些试验之后,我发现该消息仅在擦除向量的最后一个元素(ObjArr.end()-1)以外的任何内容后才会出现。 If the last element of the vector is the one erased apparently nothing bad happens. 如果向量的最后一个元素是被擦除的元素,则显然不会发生任何不良情况。 Hope these hints helped. 希望这些提示对您有所帮助。 If anyone has any idea why this happens please explain it to me. 如果有人知道为什么会发生这种情况,请向我解释。 I'm sure I'm just making a rookie mistake since this seems so easy to figure out and yet I can't. 我敢肯定我只是在犯一个新秀错误,因为这似乎很容易弄清楚,但我做不到。

The code above was compiled using Visual Studio 2013 under Windows 7. 上面的代码是使用Windows 7下的Visual Studio 2013编译的。

Interestingly, it is because you did not provide an assignment operator. 有趣的是,这是因为您没有提供赋值运算符。 Why do you need one? 为什么需要一个? Well let's think about a vector. 好吧,让我们考虑一个向量。 The vector object has a pointer to an array of 5 A_class . 向量对象具有一个指向5 A_class数组的指针。 They are default constructed, which is no problem since you've defined it. 它们是默认构造的,自定义好之后就没问题了。 Now we erase: 现在我们擦除:

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

Interestingly, we don't see a problem if we delete the last one, only if we delete one of indices 0 to 3. Why? 有趣的是,仅删除索引0到3中的一个,我们删除最后一个就不会出现问题。为什么? Well, when we delete, say, index 2, we get his: 好吧,当我们删除索引2时,我们得到了他:

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

To reconcile this space, at the end of the erase, std::vector uses the assignment operator to fix up the array. 为了协调此空间,在擦除结束时, std::vector使用赋值运算符修复数组。 So index[2] = index[3] , index[3] = index[4] . 因此index[2] = index[3]index[3] = index[4] Now, because you didn't declare an assignment operator, it will use the default, which includes deleting of index[4] . 现在,由于您没有声明赋值运算符,它将使用默认值,其中包括删除index[4] This is bad because index[4] will give index[3] its pointer then delete it , resulting in this: 这很不好,因为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

So now when we exit, we attempt to delete index[3] and everything blows up! 因此,现在退出时,我们尝试删除index[3] ,一切都炸毁了!

By adding an assignment operator that uses swap , we can fix the problem: 通过添加使用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;
    }
//...
}

When you ObjArray.erase() an object, the std::vector<A_class> will fill the produced gap. 当您使用ObjArray.erase()对象时, std::vector<A_class>将填补产生的空白。 To do so, it will move the later objects one object forward: it assigns to each object until all objects are assigned and, finally, it destroys the last element, resulting in delete[] ing that object's heap_space . 为此,它将后一个对象向前移动一个对象:它将分配给每个对象,直到分配了所有对象,最后,它销毁了最后一个元素,从而导致该对象的heap_space delete[] Note, however, since you don't have a copy or move assignment, just the pointer heap_space was assign, ie, after erase() an object the last object is in a bad state: it holds a pointer to an already delete[] ed array of bool . 但是请注意,由于没有复制或移动分配,因此仅分配了指针heap_space ,即,在erase()的对象中,最后一个对象处于错误状态:它持有指向已经delete[]的指针ed的bool数组。 When the ObjArray later goes out of scope, all objects are destroyed and a double delete[] happens. ObjArray稍后超出范围时,所有对象将被销毁,并发生double delete[] This is where you get our debug assertion. 这是您获得我们的调试断言的地方。

The easiest way to fix your problem is to provide a copy assignment: 解决问题的最简单方法是提供副本分配:

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

The above implementation leverages three operations you generally want in all classes which actually need a copy assignment: 上面的实现利用了通常在所有需要复制分配的类中通常需要的三个操作:

  1. The copy constructor takes case of creating a new copy of the assigned argument when creating the argument passed to the assignment operator. 复制构造函数会在创建传递给赋值运算符的参数时,为分配的参数创建新副本。
  2. The swap() method which exchanges the content of the current object with temporary copy in the argument. swap()方法将当前对象的内容与参数中的临时副本交换。
  3. The destructor which releases the memory of the original left-hand side as it was put into the temporary copy. 析构函数释放原始左侧存储在临时副本中的内存。

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

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