我正在尝试学习C ++,并且正在编写程序来学习复制构造函数和运算符重载。我很惊讶使用Copy构造函数时下面的程序不会崩溃说“Double Free”,而使用Operator = overloading时会一直崩溃。
#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();
}
请注意,在复制构造函数和运算符重载中,我只是将'data'指针从一个对象复制到另一个对象。当为'wd'和'wc'调用析构函数时,它会一致地崩溃程序。如果我评论下面的行并只执行复制构造函数,程序根本不会崩溃。我希望既然'wc'和'wm的数据变量也指向同一个指针,程序就会崩溃。
XHandler wd;
wd = wc;
wd.debug();
我理解双删除是未定义的行为。但是,我想知道它是一直以某种方式崩溃而不是另一种方式崩溃。
答案 0 :(得分:5)
“未定义的行为”的行为是未定义的。这意味着,没有人为这种情况付出努力,因为这种情况不应该发生。即使是工作程序也可能是“未定义的行为”。
在双重删除的特殊情况下,会发生什么,实际上取决于分配器的实现。通常的实现是将释放的内存放入列表中,并且下一个分配将满足该列表中的元素。如果双重删除一块内存,它将被添加到列表中两次。然后两个分配将满足相同的内存块,因此将在同一个内存位置创建两个对象。
BTW:你的析构函数已经坏了,因为它没有使用数组删除。
答案 1 :(得分:2)
我支持这段测试代码,希望它能在这里提供帮助,我认为暗恋很多都与平台有关,它不仅仅是偶然的,因为这段代码粉碎在linux中,但在codeblock IDE中工作正常< / p>
#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();
}
它在linux gcc4.1.2中粉碎,输出
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 ***
答案 2 :(得分:0)
您的程序有不确定的行为。
问题:
data = new char (strlen(str) + 1 );
这不会分配strlen(str) + 1
个字符。相反,它会分配一个字符并将strlen(str) + 1
值放入其中。当你之后执行strcpy时,你通过缓冲区溢出来破坏堆栈(写入strlen(str)-1 chars超过分配的空间)。要分配数组,您应该使用方括号:
data = new char [strlen(str) + 1 ];
其次,您应该使用delete[] data;
删除数据。否则,您会得到未定义的行为。
第三,strcpy代码不会复制字符串的NULL终止符。您应该添加一个空终止符:
data[ strlen(str) ] = 0;
在strcpy行之后 ; (见评论)。
此外,不要求应用程序在双重删除时崩溃。两次删除相同的内存是UB。