Frage

I've recently encountered a problem with some code I wrote in C++ which I couldn't figure out a solution for. 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.

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). 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.

War es hilfreich?

Lösung

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. 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? Well, when we delete, say, index 2, we get his:

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. So 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]. This is bad because index[4] will give index[3] its pointer then delete it, resulting in this:

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!

By adding an assignment operator that uses swap, we can fix the problem:

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

Andere Tipps

When you ObjArray.erase() an object, the std::vector<A_class> will fill the produced gap. 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. Note, however, since you don't have a copy or move assignment, just the pointer heap_space was assign, i.e., after erase() an object the last object is in a bad state: it holds a pointer to an already delete[]ed array of bool. When the ObjArray later goes out of scope, all objects are destroyed and a double delete[] happens. 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.
  3. The destructor which releases the memory of the original left-hand side as it was put into the temporary copy.
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top