我正在尝试创建一个包含动态创建变量的类。我的课目前看起来非常像这样:
class Foo
{
private:
int *dynamic_int;
public:
Foo ()
{
dynamic_int = new int;
}
~Foo ()
{
delete dynamic_int;
}
};
创建一个实例,该类不会导致任何问题。以下脚本的工作方式类似于charm:
int main ()
{
Foo a;
return 0;
}
但是,复制变量会导致程序崩溃:
int main ()
{
Foo a;
Foo b;
b = a;
return 0;
}
...返回......
*** glibc detected *** ./tester: double free or corruption (fasttop): 0x000000000081a010 ***
======= Backtrace: =========
/lib64/libc.so.6[0x3b5cc7c2d6]
./tester[0x40072b]
./tester[0x4006ca]
/lib64/libc.so.6(__libc_start_main+0xed)[0x3b5cc2169d]
./tester[0x4005c9]
======= Memory map: ========
00400000-00401000 r-xp 00000000 08:03 15074305 /home/jakob/Projects/c++ tester/tester
00600000-00601000 rw-p 00000000 08:03 15074305 /home/jakob/Projects/c++ tester/tester
0081a000-0083b000 rw-p 00000000 00:00 0 [heap]
3b5c800000-3b5c822000 r-xp 00000000 08:01 132754 /lib64/ld-2.14.90.so
3b5ca21000-3b5ca22000 r--p 00021000 08:01 132754 /lib64/ld-2.14.90.so
3b5ca22000-3b5ca23000 rw-p 00022000 08:01 132754 /lib64/ld-2.14.90.so
3b5ca23000-3b5ca24000 rw-p 00000000 00:00 0
3b5cc00000-3b5cdab000 r-xp 00000000 08:01 136046 /lib64/libc-2.14.90.so
3b5cdab000-3b5cfab000 ---p 001ab000 08:01 136046 /lib64/libc-2.14.90.so
3b5cfab000-3b5cfaf000 r--p 001ab000 08:01 136046 /lib64/libc-2.14.90.so
3b5cfaf000-3b5cfb1000 rw-p 001af000 08:01 136046 /lib64/libc-2.14.90.so
3b5cfb1000-3b5cfb6000 rw-p 00000000 00:00 0
3b5dc00000-3b5dc83000 r-xp 00000000 08:01 136047 /lib64/libm-2.14.90.so
3b5dc83000-3b5de82000 ---p 00083000 08:01 136047 /lib64/libm-2.14.90.so
3b5de82000-3b5de83000 r--p 00082000 08:01 136047 /lib64/libm-2.14.90.so
3b5de83000-3b5de84000 rw-p 00083000 08:01 136047 /lib64/libm-2.14.90.so
3b5e400000-3b5e415000 r-xp 00000000 08:01 162165 /lib64/libgcc_s-4.6.2-20111027.so.1
3b5e415000-3b5e614000 ---p 00015000 08:01 162165 /lib64/libgcc_s-4.6.2-20111027.so.1
3b5e614000-3b5e615000 rw-p 00014000 08:01 162165 /lib64/libgcc_s-4.6.2-20111027.so.1
3b64400000-3b644e9000 r-xp 00000000 08:01 167743 /usr/lib64/libstdc++.so.6.0.16
3b644e9000-3b646e8000 ---p 000e9000 08:01 167743 /usr/lib64/libstdc++.so.6.0.16
3b646e8000-3b646f0000 r--p 000e8000 08:01 167743 /usr/lib64/libstdc++.so.6.0.16
3b646f0000-3b646f2000 rw-p 000f0000 08:01 167743 /usr/lib64/libstdc++.so.6.0.16
3b646f2000-3b64707000 rw-p 00000000 00:00 0
7f94cd77e000-7f94cd783000 rw-p 00000000 00:00 0
7f94cd79f000-7f94cd7a1000 rw-p 00000000 00:00 0
7fffab7e7000-7fffab808000 rw-p 00000000 00:00 0 [stack]
7fffab881000-7fffab882000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
问题本身对我来说似乎很清楚:
为b分配值时,实例本身会被复制,但两个实例的名为“dynamic_int”的指针都指向同一个地址。在“main”函数结束时,两个实例都被销毁。这导致动态分配变量的双重删除。
我怎么能(也应该)处理这件事?处理这个问题的干净方法是什么?
答案 0 :(得分:3)
您的代码打破了rule of three:如果您有析构函数,则需要复制构造函数和赋值运算符。就目前而言,C ++“免费”提供的默认赋值运算符在两个实例的a
之间创建一个别名,因此第二个析构函数尝试释放已经释放的内存块。
您需要做的是分配一大块内存,并在赋值运算符和复制构造函数中将a
的值复制到其中。
答案 1 :(得分:1)
每个C ++类都有一个copy constructor,当需要复制该类的实例时会调用它。同样,在分配实例时(如您的示例所示),将调用assignment operator。
如果没有显式定义复制构造函数或赋值运算符,编译器会生成默认实现,只需复制所有成员。
在这种情况下,这不是正确的行为,因为Foo
只拥有dynamic_int
指针,因为它在销毁时无条件地删除它。
因此,您需要实现复制构造函数和赋值运算符来强制执行正确的复制语义,例如:
class Foo
{
private:
int *dynamic_int;
public:
Foo ()
{
dynamic_int = new int;
}
Foo (const Foo& foo)
{
dynamic_int = new int(*foo.dynamic_int);
}
Foo &operator=(const Foo& foo)
{
delete dynamic_int;
dynamic_int = new int(*foo.dynamic_int);
}
~Foo ()
{
delete dynamic_int;
}
};
rule of three最好记住并在这里很好地应用:你有特殊的破坏行为,因此你可能也需要自定义复制行为。
答案 2 :(得分:1)
如果您没有自己定义,那么C ++编译器将自动生成复制构造函数和赋值运算符。
这是您执行b=a
时的事件序列:
a
和b
现在都指向相同的dynamic_int
。main()
的右大括号时,a
和b
都会被破坏,导致在dynamic_int
指向的同一内存位置上调用删除。< / LI>
正如dasblinkenlight所提到的,如果实现了析构函数,则需要实现自己的复制构造函数和赋值运算符。
答案 3 :(得分:0)
这有两个解决方案。第一种是自己手动管理指针,这需要编写一些附加功能:
assignment: i.e. operator =
copy constructor
由于这是一个非常普遍的问题,其他人提出了解决方案,即智能指针。这些指针负责为您复制。升级库有这些可用。有几个可供选择,每个表现不同。你想要的那个取决于你想要达到的目标。您是否希望副本指向同一块内存,或者您希望每个副本指向其自己分配的内存,依此类推。