繁体   English   中英

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

[英]STL Containers & Memory Management - list of objects vs. list of pointers to objects

我已经很好地了解了关于这个主题的一些其他问题,并且没有一个(据我所知)解决了如何正确地从包含动态分配的内存的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个点(我已经免除了实现,因为这里没有问题)。 然后,类的析构函数将使用“delete”删除动态分配的数组。

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方法是否调用了对象的析构函数,并对其进行处理?

非常感谢您的帮助,

最好,

亚当

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

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);

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

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

你不需要做任何事情。

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

你不需要做任何事情。

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

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

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

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

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

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

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

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

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

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

delete *it;
myList.erase(it);

我认为以下应该有效

MyPoint* ptr = myList.back();

delete ptr;

myList.pop_back();

要么

MyPoint* ptr = myList.back();

delete ptr;

myList.erase(ptr);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM