I'd like to know why the following program gets the error "double free or corruption (fasttop)" when I run the program. I know I can use string instead of character array. But I'd like to use character array with dynamic memory allocation. Could you please let me know how I can fix this problem?
#include <iostream> #include <cstring> #include <vector> using namespace std; class Cube { public: char *str; Cube(int len) { str = new char[len+1]; } Cube(const Cube &c) { str = new char[strlen(c.str) + 1]; strcpy(str, c.str); } ~Cube() { delete [] str; } }; int main() { vector <Cube> vec; for (int i = 0; i < 10; i++) { char in [] = "hello !!"; Cube c(strlen(in)+1); strcpy(c.str, in); vec.push_back(c); } int i = 0; for ( vector<Cube>::iterator it = vec.begin(); it < vec.end(); ) { cout << it->str << endl; i++; if (i % 2 == 0) it = vec.erase(it); else it++; } for ( vector<Cube>::iterator it = vec.begin(); it < vec.end(); it++) { cout << it->str << endl; } return 0; }
You forgot to define operator=
for your class. This is the rule of Big Three (copy ctor, dtor, assignment must all be defined).
n.m. has already given a fine answer, but I found this question interesting so I decided to try to understand it a little better.
It turns out then when you call erase()
on the first item of an iterator (which we will call item0
), here's what the iterator does: it uses the =
operator of your class to do item0 = item1
. Then it deletes item1
.
If you don't define your own =
operator, I think it will simply copy the memory of your object over from item1
to item0
, so item0
and item1
will temporarily be pointing to the same string. Then when item1
gets deleted, the string gets freed, leaving item0
in an invalid state because it has a pointer to memory that has been freed.
Here is some simple test code that reproduces and illuminates the problem:
#include <cstring> #include <vector> #include <stdio.h> using namespace std; class Cube { public: char * str; Cube(const Cube &c) { set(c.str); } Cube(const char * s) { set(s); } ~Cube() { clear(); } // is "delete []" necessary? not sure #if 1 // change to 0 to cause a bug void operator=(const Cube &c) { clear(); // necessary to avoid memory leaks printf("operator=\n"); set(c.str); } #endif private: void set(const char * s) { str = new char[strlen(s) + 1]; printf("allocated %p for %s\n", str, s); strcpy(str, s); } void clear() { if (str) { printf("freeing %p: %s\n", str, str); delete str; } } }; int main(int argc, char ** argv) { printf("== CREATING VECTOR ==\n"); vector <Cube> vec; vec.push_back(Cube("octopus")); vec.push_back(Cube("squid")); printf("== BEGINNING ITERATION ==\n"); vector<Cube>::iterator it = vec.begin(); printf("First entry is %p %s\n", it->str, it->str); it = vec.erase(it); printf("Second entry is %p %s\n", it->str, it->str); // this prints garbage if Cube has no = operator return 0; }
This code produces the following output:
== CREATING VECTOR == allocated 00350F98 for octopus allocated 00350FB8 for octopus freeing 00350F98: octopus allocated 00350F98 for squid allocated 00350FD8 for squid allocated 00350FE8 for octopus freeing 00350FB8: octopus freeing 00350F98: squid == BEGINNING ITERATION == First entry is 00350FE8 octopus freeing 00350FE8: octopus operator= allocated 00350F98 for squid freeing 00350FD8: squid Second entry is 00350F98 squid freeing 00350F98: squid
I compiled and ran this in Windows with MinGW. The command I used was g++ -Wl,--enable-auto-import test.cpp && a.exe
.
If it hurts, don't do it:
#include <iostream> #include <string> #include <vector> using namespace std; class Cube { public: string str; Cube(const string& s) : str(s) { } }; int main() { vector <Cube> vec; for (int i = 0; i < 10; i++) { char in [] = "hello !!"; vec.push_back(Cube(in)); } int i = 0; for ( vector<Cube>::iterator it = vec.begin(); it < vec.end(); ) { cout << it->str << endl; i++; if (i % 2 == 0) it = vec.erase(it); else it++; } for ( vector<Cube>::iterator it = vec.begin(); it < vec.end(); it++) { cout << it->str << endl; } return 0; }
Happens to be shorter and correct (not tested).