STL容器&内存管理 - 对象列表与对象指针列表

时间:2013-03-16 14:46:40

标签: c++ list pointers memory-management stl

我已经很好地了解了关于这个主题的一些其他问题,而且(据我所知)它们都没有解决如何正确地从包含动态分配的内存的对象列表中删除项目而不是stl的对象列表不包含动态分配的内存。

我想使用一个对象列表。以此对象为例(其中不包含动态分配的内存):

class MyPoint {

public:
    MyPoint(int _x,int _y)
    {
        x = _x;
        y = _y;
    }

private:
    int x;
    int y;

};

所以我可能会创建一个对象列表(不是指向它们的指针),向它添加内容然后删除一个元素:

list<MyPoint> myList;

myList.push_back(MyPoint(3,4));
myList.push_back(MyPoint(1,2));
myList.push_back(MyPoint(8,8));
myList.push_back(MyPoint(-1,2));

list<MyPoint>::iterator it;

it = myList.begin();
advance(it,2);
myList.erase(it);

我的列表现在包含: (3,4) (1,2) (-1,2)

  • 问题1a:我是否需要对已擦除的物体进行任何其他操作或是否需要记忆?

  • 问题1b:如果程序结束,我是否需要对列表中的其余对象执行某些操作?我是否需要将它们全部删除并以某种方式处理它们的记忆?

好的,现在考虑一个允许N维空间中的点的类的替代版本。即,我可以动态地分配一个长度为N的数组来保存类中的N个点(我已经免除了实现,因为这里没有问题)。然后,类的析构函数将使用&#39; delete&#39;删除动态分配的数组。

class MyDynamicPoint {

public:
    MyDynamicPoint(int N)
    {
        points = new int[N];
    }

    ~MyDynamicPoint()
    {
        delete points;
        points = NULL;
    }

private:
    int *points;
};

我现在可能会创建一个指向对象的指针列表,而不是对象本身:

list<MyDynamicPoint*> myList;

myList.push_back(new MyDynamicPoint(8));
myList.push_back(new MyDynamicPoint(10));
myList.push_back(new MyDynamicPoint(2));
myList.push_back(new MyDynamicPoint(50));

list<MyDynamicPoint*>::iterator it;

it = myList.begin();
advance(it,2);
myList.erase(it);
  • 问题2a - 以上是否正确?即因为这个新版本的类会包含一些动态分配的内存,这是否意味着我必须创建一个指向对象的指针列表,而不是对象本身?

  • 问题2b - 鉴于我刚从列表中删除了指针,我在哪里调用delete来处理现在在对象中删除动态内存的事实?或者stl列表的erase方法是否会调用对象的析构函数,处理它?<​​/ p>

非常感谢您的任何帮助,

最佳,

亚当

4 个答案:

答案 0 :(得分:4)

如果您的类具有自动存储持续时间的数据成员(即它们的生命周期与此类的实例相关联),请执行以下操作:

class MyPoint {
private:
    int x;
    int y;
};

并且你将使用list<MyPoint> myList;,然后std::list的这个实例也是一个具有自动存储持续时间的对象,它将被自动清理,并在容器被破坏时被清除,元素也是如此它拥有。 一切都得到了解决。

但是后一个版本不是很幸运的选择...不仅你有一个容器指针,你甚至决定创建一个动态分配的类Point的数据成员。首先请注意,调用new时已分配的所有内容都应通过调用delete来释放,并且应通过调用new[]释放通过调用delete[]分配的所有内容。

在这种情况下,您在构造对象时分配内存,并在对象被破坏时进行清理:

MyDynamicPoint(int N)
{
    points = new int[N];
}
~MyDynamicPoint()
{
    delete[] points;
    points = NULL;
}
private:
int *points;

通过使用一些std::vectorstd::array而不是C风格的数组,您可以实现相同的目标,并且您不必自己处理内存管理:

MyDynamicPoint(int N) : points(std::vector<int>(N, 0)) { }

private:
std::vector<int> points;

std::vector对象将为您处理内存管理。

最后一件事:当你动态分配一个元素并将其存储到容器中时:

myList.push_back(new MyDynamicPoint(8));

你需要自己释放这个内存,从列表中删除指针是不够的:

list<MyDynamicPoint*>::iterator it;
...
delete *it;
myList.erase(it);

因此,无论您想要实现什么, 总是喜欢具有自动存储持续时间的对象(如果情况允许的话) 。没有什么比被迫手动处理内存管理和处理诸如内存泄漏等令人不快的问题更糟糕的了。

答案 1 :(得分:3)

  

问题1a:我是否需要对已删除的对象执行任何其他操作或是否需要处理内存?

您无需做任何事情。

  

问题1b:如果程序结束,我是否需要对列表中的其余对象执行某些操作?我是否需要将它们全部删除并以某种方式处理它们的记忆?

您无需做任何事情。

  

问题2a - 以上是否正确?

代码不正确。您违反了The Rule of Three。特别是,自动生成的MyDynamicPoint的复制构造函数和赋值运算符将对points指针进行按位复制。如果您复制MyDynamicPoint的实例,最终会有两个对象共享相同的points指针:

  • 当其中一个对象进入范围时,另一个对象变得无法使用。
  • 当第二个对象超出范围时,它的析构函数将尝试释放已经释放的内存。这是undefined behaviour
  

即。因为这个新版本的类会包含一些动态分配的内存,这是否意味着我必须创建一个指向对象的指针列表,而不是对象本身?

不,这并不意味着。实际上,您应该继续按值存储对象。但是,您确实需要修复三个规则。

  

问题2b - 鉴于我刚从列表中删除了指针,我在哪里调用delete来处理现在在对象中删除动态内存的事实?或者stl列表的erase方法是否会调用对象的析构函数,并对其进行处理?

由于您有一个原始指针列表,因此不会自动调用析构函数。解决这个问题的最简单方法是按值存储对象,或者使用std::unique_ptrstd::shared_ptr而不是原始指针。

答案 2 :(得分:0)

问题1,您无需做任何事情。当您按值存储对象 时,编译器和库将处理所有内容。

但是,当你按照第二种情况存储指针时,你需要delete那些用new分配的指针,否则你会有内存泄漏。

你必须在执行擦除之前删除指针,因为这会使迭代器无效:

delete *it;
myList.erase(it);

答案 3 :(得分:0)

我认为以下应该有效

MyPoint* ptr = myList.back();

delete ptr;

myList.pop_back();

OR

MyPoint* ptr = myList.back();

delete ptr;

myList.erase(ptr);