【问题标题】:Handling de-allocation of stl containers in destructors在析构函数中处理 stl 容器的解除分配
【发布时间】:2013-05-23 19:00:05
【问题描述】:

这是我第一次使用STL,我对如何释放这些容器使用的内存感到困惑。例如:

class X {
    private:
        map<int, int> a;
    public:
        X();
        //some functions
}

现在让我们说我将构造函数定义为:

X::X() {
    for(int i=0; i<10; ++i) {
        map[i]=i;
    }
}

现在我的问题是我应该为这个类编写析构函数还是默认的 C++ 析构函数将负责释放内存(完全)?

现在考虑对上面的类进行修改

class X {
    private:
        map<int, int*> a;
    public:
        X();
        ~X();
        //some functions
}

现在让我们说我将构造函数定义为:

X::X() {
    for(int i=0; i<10; ++i) {
        int *k= new int;
        map[i]=k;
    }
}

现在我明白,对于这样一个类,我需要编写一个析构函数,因为 new 分配的内存不能被map 容器的默认析构函数破坏(因为它调用对象的析构函数,在这种情况下是一个指针)。所以我尝试编写以下析构函数:

X::~X {
    for(int i=0; i<10; ++i) {
        delete(map[i]);
    }
    //to delete the memory occupied by the map.
}

不知道怎么删除map占用的内存。虽然有clear 函数,但它声称将容器的大小降低到0,但不一定释放下面的内存。向量也是如此(我猜是 STL 中的其他容器,但我没有检查它们)。

任何帮助表示赞赏。

【问题讨论】:

    标签: c++ memory stl constructor destructor


    【解决方案1】:

    如果您忽略您正在处理的容器类型,而将其视为一个容器,您会注意到您放入容器中的任何东西都归拥有该容器的人所有。这也意味着删除该内存取决于所有者。您的方法足以释放您分配的内存。因为map对象本身是一个栈分配的对象,它的析构函数会被自动调用。

    或者,这种情况的最佳实践是使用 shared_ptr 或 unique_ptr,而不是原始指针。这些包装类会自动为您释放内存。

    map<int shared_ptr<int>> a;
    

    http://en.cppreference.com/w/cpp/memory

    【讨论】:

      【解决方案2】:

      当一个 STL 集合被销毁时,所包含对象的相应析构函数被调用。

      这意味着如果你有

      class YourObject {
        YourObject() { }
        ~YourObject() { }
      }
      
      map<int, YourObject> data;
      

      然后调用YourObject的析构函数。

      另一方面,如果您要存储指向对象的指针,例如 in

      map<int, YourObject*> data
      

      然后调用指针的析构,释放指针本身但不调用指向的构造函数。

      解决方案是使用可以容纳您的对象的东西,例如shared_ptr,这是一个特殊的对象,当没有更多对它的引用时,它会关心调用所持有的项目对象。

      例子:

      map<int, shared_ptr<YourObject>>
      

      【讨论】:

        【解决方案3】:

        简短的回答是,当容器本身被销毁时,容器通常会负责删除其内容。

        它通过销毁容器中的对象来做到这一点。因此,如果您非常想这样做,您可以创建一个通过分配内存(例如,在其 ctor 中)管理其内存但没有正确释放它的类型。这显然应该通过更正这些对象的设计来解决(例如,添加一个释放它们拥有的内存的 dtor)。或者,您可以通过仅存储原始指针来获得相同的效果。

        同样,您可以创建一个不能正常工作的分配器——它分配了内存,但在被要求释放内存时什么也不做。

        在每一种情况下,真正的答案都是“不要那样做”。

        【讨论】:

          【解决方案4】:

          我应该为这个类编写析构函数还是默认的 C++ 析构函数将负责释放内存(完全)?

          是的,它会的。所有标准容器都遵循RAII的原则,管理自己的动态资源。当它们被销毁时,它们将自动释放它们分配的任何内存。

          不知道怎么删除map占用的内存。

          你没有。当且仅当您使用new 创建某些内容时,您必须删除它。大多数对象的内存都会自动分配和释放。

          地图本身嵌入在被销毁的X对象中,所以它会被自动销毁,一旦析构函数完成,它的内存将与对象的内存一起被释放。

          map分配的任何内存都是map的责任;它会在自动调用的析构函数中释放它。

          您只负责删除动态分配的int 对象。由于很难确保正确删除它们,因此您应该始终使用 RAII 类型(例如智能指针或映射本身)来为您管理内存。 (例如,如果使用 new 引发异常,则您的构造函数中存在内存泄漏;这很容易通过存储对象或智能指针而不是原始指针来解决。)

          【讨论】:

          • 嗯,答案很明确,但删除我的意思不是delete。我的意思是破坏内存(否则我会把它放在反引号中)。我的意思是我在 STL 文档中没有看到这样做的功能。还是谢谢你。
          • @AmanDeepGautam:与任何对象一样,没有函数可以释放其内存。对象根据其生命周期被销毁并释放内存:超出范围时的自动对象、程序结束时的静态对象以及删除时的动态对象。
          • 这让我很困惑。我现在明白了。谢谢。
          【解决方案5】:

          如果您必须编写析构函数(或 cctor 或 op=),则表明您可能做错了什么。如果您这样做是为了释放资源,则更有可能如此。

          例外是资源的 RAII 处理程序,它什么也不做。

          在常规类中,您使用适当的成员和基类,因此您的 dtor 没有自己的工作。

          STL 类都自行处理,因此拥有地图您无需承担任何义务。除非你用指向已分配内存的愚蠢指针或类似的东西填充它——第一个观察开始的地方。

          你的第二个 X::X() 样本在很多方面都被破坏了,如果在第 5 次抛出异常,新的你会泄漏前 4 次。如果你想手动处理这种情况,你最终会得到一团糟的代码.
          如果您使用适当的智能事物,例如 unique_ptr 或 shared_ptr 而不是 int*,这一切都可以避免。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2021-06-12
            • 2013-12-14
            • 2018-06-30
            • 1970-01-01
            • 2012-09-15
            • 2010-12-11
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多