std :: pair和class destructors

时间:2012-01-17 15:28:31

标签: c++ map destructor std-pair

  

可能重复:
  What is The Rule of Three?

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 ......的一些事情?

提前感谢所有人!

4 个答案:

答案 0 :(得分:10)

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

请查看 Rule of three

如果你担心效率,在C ++ 11中也会看move constructor

答案 1 :(得分:7)

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

答案 2 :(得分:4)

您必须定义合适的复制构造函数,因为类的副本通过复制的指针实例共享相同的数组。

答案 3 :(得分: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 ++习语),并了解{ {3}}(注意:您可以免费获得GotW系列文章中的大量书籍内容。)