2

这是我第一次使用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 中的其他容器,但我没有检查它们)。

任何帮助表示赞赏。

4

5 回答 5

4

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

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

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

你没有。当且仅当您使用new. 大多数对象都会自动分配和释放它们的内存。

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

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

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

于 2013-05-28T17:49:40.807 回答
2

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

这意味着如果你有

class YourObject {
  YourObject() { }
  ~YourObject() { }
}

map<int, YourObject> data;

YourObject然后调用析构函数。

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

map<int, YourObject*> data

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

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

例子:

map<int, shared_ptr<YourObject>>
于 2013-05-28T17:47:55.587 回答
1

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

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

map<int shared_ptr<int>> a;

请参阅http://en.cppreference.com/w/cpp/memory

于 2013-05-28T17:47:39.957 回答
0

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

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

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

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

于 2013-05-28T17:49:10.973 回答
0

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

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

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

STL 类都自己处理,因此拥有地图您没有义务。除非你用指向已分配内存的哑指针或类似的东西填充它——第一个观察开始的地方。

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

于 2013-05-28T18:03:54.913 回答