如何在类中处理动态分配的变量?

时间:2011-12-24 11:28:03

标签: c++ class dynamic new-operator

我正在尝试创建一个包含动态创建变量的类。我的课目前看起来非常像这样:

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”函数结束时,两个实例都被销毁。这导致动态分配变量的双重删除。

我怎么能(也应该)处理这件事?处理这个问题的干净方法是什么?

4 个答案:

答案 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的逐位浅拷贝
  • 因此,ab现在都指向相同的dynamic_int
  • 当您点击main()的右大括号时,ab都会被破坏,导致在dynamic_int指向的同一内存位置上调用删除。< / LI>

正如dasblinkenlight所提到的,如果实现了析构函数,则需要实现自己的复制构造函数和赋值运算符。

答案 3 :(得分:0)

这有两个解决方案。第一种是自己手动管理指针,这需要编写一些附加功能:

assignment: i.e. operator =
copy constructor

由于这是一个非常普遍的问题,其他人提出了解决方案,即智能指针。这些指针负责为您复制。升级库有这些可用。有几个可供选择,每个表现不同。你想要的那个取决于你想要达到的目标。您是否希望副本指向同一块内存,或者您希望每个副本指向其自己分配的内存,依此类推。