问题
I am trying to learn C++ and was writing programs to learn Copy Constructors and Operator overloading. I am surprised that the below program when Copy constructor is used doesn't crash saying "Double Free", whereas when using Operator = overloading crashes consistently.
#include <iostream>
using namespace std;
class XHandler
{
public:
XHandler()
{
data = new char[8];
strcpy(data, "NoName");
}
XHandler (const char *str)
{
data = new char (strlen(str) + 1 );
strcpy (data, str);
}
XHandler (const XHandler &xh)
{
data = xh.data;
}
XHandler& operator = (const XHandler &xh)
{
data = xh.data;
}
~XHandler()
{
delete data;
}
void debug()
{
cout << data <<endl;
}
private:
char *data;
};
int main()
{
XHandler wm("hello"), wb("there");
wm.debug();
wb.debug();
XHandler wc (wm);
wc.debug();
XHandler wd;
wd = wc;
wd.debug();
}
Note that in both copy constructor and operator overloading, I am just copying the 'data' pointer from one object to another. When the destructor is invoked for 'wd' and 'wc', it crashes the program consistently. If I comment the below lines and execute only the copy constructor, the program doesn't crash at all. I would expect since both 'wc's and 'wm's data variable are also pointing to the same pointer, the program would crash.
XHandler wd;
wd = wc;
wd.debug();
I understand double delete is undefined behaviour. But, what I wonder is it consistently crashes in one way and doesn't in the other.
回答1:
The behavior of "Undefined behavior" is just undefined. That means, no one put effort into that cases because that cases should not happen. Even a working program can be "Undefined behavior".
In your special case of an double delete, what happens, really depends on the implementation of the allocator. A usual implementation is to put released memory into a list and a next allocation will be satisfied with an element out of this list. If you double delete a chunk of memory, it will be added to the list twice. And then two allocation will be satisfied with the very same chunk of memory and thus two object will be created at the same memory location.
BTW: You destructor is already broken, as it doesn't use the array delete.
回答2:
I wirte this piece of test code, hope it will help here, I think crush is related to platform a lot, it is not just by chance, since this piece of code crush in linux ,but works fine in codeblock IDE
#include <iostream>
#include <cstring>
#include <stdio.h>
using namespace std;
class XHandler
{
public:
XHandler()
{
data = new char[8];
strcpy(data, "NoName");
std::cout<< "default construcor is called" << std::endl;
}
XHandler (const char *str)
{
data = new char [strlen(str) + 1 ];
strcpy (data, str);
std::cout<< "param construcor is called" << std::endl;
}
XHandler (const XHandler &xh)
{
data = xh.data;
std::cout<< "copy construcor is called" << std::endl;
}
XHandler& operator = (const XHandler &xh)
{
data = xh.data;
std::cout<< "operator construcor is called" << std::endl;
return *this;
}
~XHandler()
{
std::cout<< "destrucor is called" << std::endl;
print_dir();
if (data)
{
delete [] data;
data = NULL;
std::cout<< "delete data" << std::endl;
}
}
void debug()
{
cout << data <<endl;
}
void print_dir()
{
printf("location: %p\n",data);
}
private:
char *data;
};
int main()
{
XHandler wm("hello"), wb("there");
wm.debug();
wb.debug();
wm.print_dir();
wb.print_dir();
XHandler wc (wm);
wc.print_dir();
wc.debug();
XHandler wd;
wd = wc;
wd.debug();
}
it crush in linux gcc4.1.2 ,it outputs
param construcor is called
param construcor is called
hello
there
location: 0x502010
location: 0x502030
copy construcor is called
location: 0x502010
hello
default construcor is called
operator construcor is called
hello
destrucor is called
location: 0x502010
delete data
destrucor is called
location: 0x502010
*** glibc detected *** ./test: double free or corruption (fasttop): 0x0000000000502010 ***
回答3:
Your program has undefined behavior.
Problems:
data = new char (strlen(str) + 1 );
This doesn't allocate strlen(str) + 1 characters. Instead, it allocates a single character and places the strlen(str) + 1 value within. When you perform a strcpy afterwards, you are corrupting the stack through buffer overflow (writing strlen(str) -1 chars past the allocated space). To allocate an array you should use square brackets:
data = new char [strlen(str) + 1 ];
Second, you should delete the data with delete[] data;. Otherwise, you get undefined behavior.
Third, your strcpy code doesn't copy the NULL terminator of the string. You should add a null terminator:
data[ strlen(str) ] = 0;
after the strcpy lines; (see comments).
Additionally, there is no requirement that an application would crash on double delete. Deleting the same memory twice is UB.
来源:https://stackoverflow.com/questions/21849298/double-delete-of-data-doesnt-crash