关于c ++:std :: pair和class destructors

std::pair and class destructors

本问题已经有最佳答案,请猛点这里访问。

Possible Duplicate:
What is The Rule of Three?

std::pair如何为其组件调用析构函数?我试图将一个类的实例添加到一个std::map中,但是我在类的析构函数方面遇到了错误。

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

下面,my_class只是在构建时创建一个int数组,并在销毁时删除它。不知怎么的,我得到了一个"双重删除"错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//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

同时,在主.cpp中…

1
2
3
4
5
6
7
8
9
//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

编译正常,但这会生成以下运行时错误:

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

或者,使用Valgrind:

1
2
3
4
5
6
==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)

(因为我删掉了评论和内容,所以行号被取消)

我一定是错过了一些关于EDOCX1的东西。

感谢所有人的事先准备!


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

请看三条规则

在C++ 11中,如果你担心效率,也可以看看移动构造函数。


类违反了三条规则,定义了一个不带复制构造函数和赋值运算符的析构函数。一旦定义了这些,代码就可以正常运行了:STL容器严重依赖于这些,所以您应该问问自己,在每次使用类作为STL容器的模板参数时,是否都实现了这三个参数。


三人法则是幻想。标准的容器通常更漂亮。

问题是没有复制数组,而是复制指向数组的指针。现在,如果两个实例具有相同的指针,则将删除同一个数组两次。

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

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

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

所以:

一般来说,与手动数组相比,首选标准容器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
};

如果您必须省略标准容器:

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

BUF在这样做之前,阅读关于三的规则,注意自我分配的危险,知道交换技巧(注意:这是一个常见的C++习语),并且学习异常安全性(注意:你会在GOTW系列文章中找到很多免费的内容)。


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