我有一个类Foo
定义如下:
class Foo
{
public:
Foo(int num);
Foo(const Foo& other);
~Foo();
Foo& operator=(const Foo& other);
...
private:
string *fooArray;
void clearMemory();
...
}
Foo::Foo(int num)
{
fooArray = new string[num];
}
Foo::Foo(const Foo& other)
{
*this = other;
}
Foo::~Foo()
{
clearMemory();
}
Foo& operator=(const Foo& other)
{
clearMemory();
fooArray = new string[other.size]; // size is a private variable
memcpy(fooArray, other.fooArray, other.size * sizeof(string));
}
void Foo::someOtherFooFuncion(int newNum)
{
clearMemory(); // crash with Visual Studio, does not crash with g++, but g++ also
// crashes when destructor is called
fooArray = new string[newNum];
}
void Foo::clearMemory()
{
if(fooArray != NULL)
{
delete[] fooArray; // sometimes it crashes here with g++; it always crashes when
// compiling in Visual Studio
fooArray = NULL;
}
}
如代码注释中所述,它有时会让我崩溃。我已经尝试过按照GDB中的步骤进行操作
destructing Foo:
@0x7fff5fbff9b0
$26 = {
fooArray = 0x1001009d8,
...
}
然后到达delete[] fooArray
,突然
Foo(49858) malloc: *** error for object 0x100100990: pointer being freed was not allocated
不知道0x100100990来自哪里。
我意识到代码非常不完整,但我真的不知道现在哪里开始寻找bug,并且想知道哪些可能的条件会导致delete[]
错误。
修改:
添加了c'tor,d'tor和赋值运算符。我远离PC,因此代码可能不是100%准确。将值赋给fooArray并访问它们的工作正常。
另外,我非常感谢可能导致delete[]
抛出错误的一般问题列表,这样我至少可以知道在哪里查看代码。
编辑2 :
所以我遵循Xeo的建议使用std::uninitialized_copy
,现在代码工作正常并在g ++下编译。析构函数也可以在Visual Studio中正常工作,但以某种方式删除someOtherFooFuncion
中的fooArray会使其崩溃。
还有其他想法吗?
答案 0 :(得分:2)
确保定义复制构造函数和赋值运算符。他们需要为fooArray
分配内存。如果您没有定义copy ctor和operator=
,编译器将为您生成它们。但是,编译器生成的只会复制fooArray
指针,可能导致双deletes
。
如果您已经定义了它们,请在您的问题中添加相关代码。
编辑:我可以看到您的复制构造函数没有分配内存,而您的赋值运算符正在使用memcpy()
。两者都可能导致问题。
答案 1 :(得分:1)
如果定义默认构造函数,则必须将fooArray
初始化为NULL,否则fooArray
可能指向随机内存位置,然后该位置受delete[]
的限制。
答案 2 :(得分:1)
我不能强调这一点。当{C}中的memcpy
类对象(确切地说是非POD)时,你打破了由其构造函数定义的那个类'不变量,因为你正在规避那些。每次memcpy
一个std::string
类时,您都会获得一个新对象,将相同内存作为另一个对象。你会得到一个双重删除,这会导致你的崩溃。
使用std::uninitialized_copy
中的<algorithm>
,如下所示:
// src_begin src_end dest
std::uninitialized_copy(other.array, other.array + other.size, array);
(未经测试,因为我是从iPod写的)
甚至更好,只需使用std::vector
而不是原始内存。您将不再需要析构函数和复制ctor /赋值运算符。