【问题标题】:Proper way to destroy a map that has pointer values销毁具有指针值的映射的正确方法
【发布时间】:2013-07-25 13:07:45
【问题描述】:

我正在使用std::map 将字符串值映射到MyType *。我的地图声明如下所示:

map<string, MyType *> *my_map = new map<string, MyType>;

my_map 是我的一个类的私有成员变量。我的问题是我不确定如何销毁地图。删除地图时,我还想在地图中包含的所有MyType * 上调用delete。这是我当前的析构函数:

my_map->erase(my_map->begin(), my_map->end());
delete my_map;

这会删除映射中包含的指针,还是我需要在调用擦除之前遍历映射以删除每个指针?

【问题讨论】:

  • 后者 - map(以及标准库中的大多数(如果不是全部?)容器)并未设计为在销毁时删除它包含的任何指针。
  • 好的,谢谢。我阅读的文档不是很清楚。上面写着:This effectively reduces the container size by the number of elements removed, which are destroyed.
  • 这是一个常见的误解 - 指针 本身确实被破坏了,但这不会改变它们指向的内存位置的状态。
  • 为什么痴迷于指针和new?这有什么问题:map&lt;string, MyType&gt; my_map;
  • 设计出了点问题。为什么容器有自己的指针?

标签: c++ pointers stl


【解决方案1】:

指针只是指向。使用原始指针时,您需要知道应用程序的哪个部分拥有指针指向的资源。如果它们归地图所有,您将需要遍历地图并在地图被销毁之前对每个指针调用 delete。但是,如果映射只包含指向代码其他部分所拥有的对象的指针,则无需执行任何操作。

更安全的解决方案是使用 shared_ptr 来管理对象的生命周期,这将确保在最后一个 shared_ptr 被销毁时正确删除对象。您可以将 shared_ptr 存储在地图中,如果没有其他 shared_ptr 实例引用地图中的对象,则在地图被销毁时,这些对象将根据需要被销毁。

【讨论】:

    【解决方案2】:

    如果您使用smart pointers 而不是原始指针,所有内容都会自动为您清理。

    // header:
    using MapType = std::map<std::string, std::shared_ptr<MyType>>;
    shared_ptr<MapType> my_map;
    
    // usage:
    my_map.emplace("foo", std::make_shared<MyType>());
    
    // destructor:
    MyClass::~MyClass()
    {
        // nothing!
    }
    

    【讨论】:

      【解决方案3】:

      这会删除地图中包含的指针[...]吗?

      不,鉴于您提供的代码,您将泄露地图的每个成员。

      通常,对于每个new,都必须有一个匹配的delete。您有一个 delete 用于地图,但没有用于其中的元素。

      解决这个问题最正确的方法是根本不使用动态分配。如果可能,只需存储MyTypes 目录:

      map&lt;string, MyType&gt;

      ... 而不是动态分配 map 本身,而是自动存储它:

      map<string,MyType> my_map;
      

      如果由于某种原因无法自动存储持续时间,则使用智能指针进行动态分配。给定一个 C++11 编译器,对map 中的元素使用unique_ptr(或者,很少使用shared_ptr 甚至weak_ptr):

      map<string, unique_ptr<MyType>> my_map;
      

      (给定一个 C++03 编译器,使用它的 Boost 等价物。)然后当 my_map 被销毁时,所有元素将是 deleted。

      排除所有这些,如果您处于上述任何一种情况都不适合您的情况(我高度怀疑),那么您将需要自己迭代地图:

      struct deleter
      {
        template <typename T> operator() (const T& rhs) const
        { 
          delete rhs.second;
        }
      };
      
      for_each (my_map->begin(), my_map->end(), deleter());
      

      在 C++11 中,这可以做成一个 lambda,类似于:

      for_each (my_map->begin(), my_map->end(), [](auto item) -> void
      {
        delete item.second;
      });
      

      【讨论】:

        【解决方案4】:

        在现代 C++ 中,让您的生活更轻松,仅在严格要求时使用指针

        您从以下代码开始:

        map<string, MyType *> *my_map = new map<string, MyType>;
        

        您可以做的第一件事是考虑使用std::map 实例作为数据成员,而不是指向它的指针

        然后,如果MyType 的复制成本不是很高,并且它的实例仅由地图拥有,只需考虑一个简单的mapstringMyType(而不是MyType*):

        // my_map data member - no pointers --> automatically deleted in class destructor
        map<string, MyType> my_map;
        

        如果您确实需要包含指针的映射,请考虑使用 智能指针,例如 std::shared_ptr(在 C++11/14 中可用)用于共享所有权,或 std::unique_ptr 用于唯一非共享所有权。
        (如果你的目标是 C++98/03,一个选项是使用boost::shared_ptr。由于没有移动语义,你不能有unique_ptr,它很大程度上基于移动语义特性。)
        例如:

        // Map containing _smart_ pointers 
        //     --> default destructor is fine (no need for custom delete code)
        map<string, shared_ptr<MyType>> my_map;
        

        如您所见,使用值语义(而不是原始指针)或智能指针,您可以简化代码并使用自动销毁 由 C++ 提供。

        【讨论】:

          猜你喜欢
          • 2012-11-30
          • 2013-11-27
          • 1970-01-01
          • 2014-11-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多