2011-12-24 61 views
0

我想创建一个包含动态创建变量的类。我班目前看起来非常像这样:如何处理类中的动态分配变量?

class Foo 
{ 
    private: 
    int *dynamic_int; 
    public: 
    Foo() 
    { 
     dynamic_int = new int; 
    } 
    ~Foo() 
    { 
     delete dynamic_int; 
    } 
}; 

创建一个实例的类不会引起问题。下面的脚本作品般的魅力:

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

我该如何处理这个问题?什么是处理这个干净的方式?

回答

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是好事,记住并应用以及在这里:你有特殊销毁行为,因此您可能还需要自定义复制行为。

3

你的代码打破了rule of three:如果你有一个析构函数,你需要一个拷贝构造函数和一个赋值操作符。就目前而言,C++“免费”提供的默认赋值运算符会在两个实例的a之间创建一个别名,因此第二个析构函数会尝试释放已释放的内存块。

你需要做的是分配一块新的内存,并将a的值复制到它的赋值操作符和复制构造函数中。

0

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

assignment: i.e. operator = 
copy constructor 

因为这是一个非常普遍的问题,其他人都拿出了解决方案,即智能指针。这些是照顾你的复制的指针。助推库有这些可用。有几种可供选择,每种都有不同的表现。你想要的那个取决于你想要达到的目标。你想让副本指向同一块内存,还是希望每个内存都指向它自己分配的内存,等等。

1

A C++编译器会自动生成一个拷贝构造函数赋值运算符,如果你不自己定义一个。

因此,这是事件发生的顺序,当你做b=a

  • 默认赋值运算符被调用时,造成的一个逐位浅拷贝到b中。
  • 因此,ab现在都指向相同的dynamic_int
  • 当您敲击main()的大括号时,ab都被破坏,导致在dynamic_int指向的同一内存位置上调用删除。

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