2012-01-17 102 views
0

Possible Duplicate:
What is The Rule of Three?的std ::对,类析构函数

为什么说std::pair调用析构函数及其组成部分?我正在尝试将一个类的实例添加到std::map,但是我的类的析构函数出现错误。

我已经将我的问题缩小到以下非常简单的例子。

下面,my_class仅在构造时创建了一个int数组,并在销毁时将其删除。不知怎的,我得到一个“双删除”错误:

//my_class.h 
class my_class { 
    public: 
    int an_int; 
    int *array; 

    //constructors: 
    my_class() 
    { 
     array = new int[2]; 
    } 
    my_class(int new_int) : an_int(new_int) 
    { 
     array = new int[2]; 
    } 

    //destructor: 
    ~my_class() 
    { 
     delete[] array; 
    } 
}; //end of my_class 

同时,比main.cpp中......

//main.cpp 
int main(int argc, char* argv[]) 
{ 
    std::map<int, my_class> my_map; 

    my_map.insert(std::make_pair<int, my_class> (1, my_class(71))); 

    return 0; 
} // end main 

编译去罚款,但这种生成以下运行时错误:

*** glibc detected *** ./experimental_code: double free or corruption (fasttop): 

或者,用的valgrind:

==15258== Invalid free()/delete/delete[]/realloc() 
==15258== at 0x40249D7: operator delete[](void*) (vg_replace_malloc.c:490) 
==15258== by 0x8048B99: main (my_class.h:38) 
==15258== Address 0x42d6028 is 0 bytes inside a block of size 8 free'd 
==15258== at 0x40249D7: operator delete[](void*) (vg_replace_malloc.c:490) 
==15258== by 0x8048B91: main (my_class.h:38) 

(行号已关闭,因为我删除了评论和内容)

我必须缺少一些关于std::pair ...?

感谢所有提前!

+0

为什么不使用'int array [2]'而不是'int * array'? – Pubby 2012-01-17 15:31:11

+9

那么,[你的拷贝构造函数和拷贝赋值操作符在哪里?](http://stackoverflow.com/q/4172722/500104) – Xeo 2012-01-17 15:31:12

+2

请注意,如果你不需要拷贝构造函数或拷贝赋值操作符直接分配内存。改为使用'std :: vector an_array'。 – 2012-01-17 15:36:27

回答

9

当您将my_class添加到stl容器时,会调用复制构造函数。由于你没有定义一个它做了成员复制和两个my_class对象被创建指向同一个int数组,当这些被删除的同一int数组可能会被删除两次

请看看Rule of three

如果您担心效率问题,那么在C++ 11中也会看到move constructor

+0

谢谢。我甚至都不知道复制构造函数存在 - 当你仅仅从在线论坛学习C++时会发生什么:( – cmo 2012-01-17 17:05:52

+1

@CycoMatto如果你正在学习一个狂欢,还有谷歌的C++中的“移动构造函数”11 – 2012-01-17 17:14:51

+0

@CycoMatto:在开始的时候,我也通过在线资源学习了C++,我可以告诉你这在C++中是非常危险的,如果你不知道正确的资源,那么很多有害的代码就在那里。在我的答案),例如。并看看[列表](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list)。 – 2012-01-18 07:10:00

4

你必须定义一个合适的拷贝构造函数,因为你的类的副本通过指针的拷贝实例共享相同的数组。

7

你的类违反the rule of three通过定义一个没有复制构造函数和赋值运算符的析构函数。一旦你定义了这些,你的代码应该运行OK:STL容器很大程度上依赖于这些,所以你应该问问自己,你是否每次使用类作为STL容器的模板参数都实现了全部三个。

4

三条规则很花哨。标准容器通常更有趣。


问题是不是数组被复制,而是指向它们的指针。现在,如果两个实例保持相同的指针,那么您将删除同一个数组两次。

您可以为您的课程定义适当的复制操作,但通常使用标准容器可以解决复制,内存获取,内存释放,自我分配,异常保证等所有问题。

  • 使用std::vector作为动态数组的替代替代品。
  • 使用std::array作为固定大小数组的替代替代品。

如果您的所有成员都有适当的复制语义,您的类甚至不需要显式复制操作,因此您可以节省大量工作并提高可维护性并减少错误机会。

所以:

一般来说,比手动阵列喜欢标准集装箱:

class my_class { 
public: 
    my_class() 
    : new_int(0), array(2) 
    {} 

    my_class(int new_int) 
    : an_int(new_int), array(2) 
    {} 

private: 
    int an_int; 
    std::vector<int> array; // do not expose them 
}; 

class my_class { 
public: 
    my_class() 
    : new_int(0) 
    {} 

    my_class(int new_int) 
    : an_int(new_int) 
    {} 

private: 
    int an_int; 
    std::array<int,2> array; // do not expose them 
}; 

IFF必须省略标准集装箱:

  • 写一个拷贝构造函数。
  • 书写副本分配。 或
  • 完全禁止复制。

BUF在这样做之前,了解rule of three,注意self assignment的危害,知道swap trick(注:这是一个常见的C++成语),并了解exception safety(注:你会发现很多这本书的内容在GotW系列文章中是免费的)。

+1

您的答案改变原来的如果你没有看到它,试着打印你的类和原始类的'sizeof()',这在我的机器上有28个字节的差异 - 从你的解决方案从原来的8到36,考虑到作者只想保持2 'int'在那里,你不觉得28字节的额外开销来容纳8个字节一个BIT太多了?我的意思是,你的'std :: vector '的大小本身比原来的类PLUS分配的内存大两倍。 – lapk 2012-01-17 16:42:40

+0

@AzzA优秀的一点。在真正的程序中,有了真实的类,将会有几千个类的实例(并且有许多成员变量未显示)。所以我想这额外的开销是值得担心的... – cmo 2012-01-17 16:52:20

+0

@AzzA:首先:改变它戏剧是我的意图:免费的免费,内存管理的异常安全,免费复制语义;看到,几乎没有代码来管理数据,但它在那里。当时间/金钱很重要时,那么用所有样板书写成千上万行正确的代码是很昂贵且容易出错的。手动管理有它的用例。但那是在特殊情况下,而不是在一般情况下。 – 2012-01-18 06:53:41

相关问题