简体   繁体   English

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

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

I have had a good look at some other questions on this topic and none of them (to my knowledge) address how to correctly erase items from a stl list of objects which contain dynamicically assigned memory vs. a stl list of objects that don't contain dynamically assigned memory. 我已经很好地了解了关于这个主题的一些其他问题,并且没有一个(据我所知)解决了如何正确地从包含动态分配的内存的stl列表中删除项目与不包含动态分配的内存的stl列表包含动态分配的内存。

I want to use a list of objects. 我想使用一个对象列表。 Take this object for example (which contains no dynamically assigned memory): 以此对象为例(其中不包含动态分配的内存):

class MyPoint {

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

private:
    int x;
    int y;

};

So I might create a list of objects (not pointers to them), add things to it and then erase an element: 所以我可能会创建一个对象列表(不是指向它们的指针),向它添加内容然后删除一个元素:

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

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

  • QUESTION 1a: do I need to do anything else to the erased object or will the memory be taken care of? 问题1a:我是否需要对已删除的对象执行任何其他操作或是否需要处理内存?

  • QUESTION 1b: if the program ends, do I need to do something with the remaining objects in the list? 问题1b:如果程序结束,我是否需要对列表中的其余对象执行某些操作? Do I need to delete them all and deal with their memory somehow? 我是否需要将它们全部删除并以某种方式处理它们的记忆?

Ok, now consider an alternative version of the class that allowed a point in N-dimensional space. 好的,现在考虑一个允许N维空间中的点的类的替代版本。 Ie, I could dynamically assign an array of length N to hold the N points inside the class (I have spared you the implementation as that is not in question here). 也就是说,我可以动态地分配一个长度为N的数组来保存类中的N个点(我已经免除了实现,因为这里没有问题)。 The destructor of the class would then delete the dynamically assigned array using 'delete'. 然后,类的析构函数将使用“delete”删除动态分配的数组。

class MyDynamicPoint {

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

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

private:
    int *points;
};

I might now create a list of pointers to the objects, instead of the objects themselves: 我现在可能会创建一个指向对象的指针列表,而不是对象本身:

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);
  • QUESTION 2a - Is the above correct? 问题2a - 以上是否正确? Ie Because this new version of the class would contain some dynamically assigned memory, does this mean I have to create a list of pointers to objects, not the objects themselves? 即因为这个新版本的类将包含一些动态分配的内存,这是否意味着我必须创建一个指向对象的指针列表,而不是对象本身?

  • QUESTION 2b - Given that I have just erased the pointer from the list, where do I call delete to deal with the fact there is now dynamic memory to be deleted in the objects? 问题2b - 鉴于我刚刚从列表中删除了指针,我在哪里调用delete来处理现在在对象中删除动态内存的事实? Or does the erase method of stl list call the destructor of the object, taking care of it? 或者stl列表的erase方法是否调用了对象的析构函数,并对其进行处理?

Many thanks in advance for any help, 非常感谢您的帮助,

Best, 最好,

Adam 亚当

When you have a class with data members that have automatic storage duration (ie their lifetime is tied to the instance of this class) like this: 如果您的类具有自动存储持续时间的数据成员(即它们的生命周期与此类的实例相关联),如下所示:

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

and you will use list<MyPoint> myList; 你将使用list<MyPoint> myList; , then this instance of std::list is also an object with automatic storage duration, that will be cleaned up automatically and by the time the container is destructed, so are the elements it holds. ,那么std::list这个实例也是一个具有自动存储持续时间的对象,它将被自动清理,并且当容器被破坏时,它所拥有的元素也是如此。 Everything is taken care of. 一切都得到了照顾。

But the latter version is not very lucky choice... not only that you have a container holding pointers, you even decided to create a data member of class Point that will be allocated dynamically. 但是后一个版本不是很幸运的选择...不仅你有一个容器指针,你甚至决定创建一个动态分配的Point类数据成员。 At first note that everything that has been allocated by calling new should be freed by calling delete and everything allocating by calling new[] should be freed by calling delete[] . 首先请注意,调用new分配的所有内容都应该通过调用delete来释放,通过调用new[]分配的所有内容都应该通过调用delete[]来释放。

In this situation, you are allocating the memory when the object is constructed and cleaning it up when the object is destructed: 在这种情况下,您将在构造对象时分配内存,并在对象被销毁时进行清理:

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

You would achieve the same by using some std::vector or std::array instead of the C-style array and you wouldn't have to take care of the memory management on your own: 你可以通过使用一些std::vectorstd::array而不是C风格的数组来实现同样的目的,你不必自己处理内存管理:

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

private:
std::vector<int> points;

the std::vector object will take care of memory management for you. std::vector对象将为您处理内存管理。

And last thing: when you dynamically allocate an element and store it into the container: 最后一件事:当您动态分配元素并将其存储到容器中时:

myList.push_back(new MyDynamicPoint(8));

you need to free this memory on your own, erasing the pointer from the list is not enough: 你需要自己释放这个内存,从列表中删除指针是不够的:

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

So whatever you want to achieve, always prefer objects with automatic storage duration if the situation allows it . 因此,无论您想要实现什么, 如果情况允许总是喜欢具有自动存储持续时间的对象 There's nothing worse than being forced to taking care of memory management manually and dealing with unpleasant problems such as memory leaks later. 没有什么比被迫手动处理内存管理和处理诸如内存泄漏之类的令人不快的问题更糟糕的了。

QUESTION 1a: do I need to do anything else to the erased object or will the memory be taken care of? 问题1a:我是否需要对已删除的对象执行任何其他操作或是否需要处理内存?

You don't need to do anything. 你不需要做任何事情。

QUESTION 1b: if the program ends, do I need to do something with the remaining objects in the list? 问题1b:如果程序结束,我是否需要对列表中的其余对象执行某些操作? Do I need to delete them all and deal with their memory somehow? 我是否需要将它们全部删除并以某种方式处理它们的记忆?

You don't need to do anything. 你不需要做任何事情。

QUESTION 2a - Is the above correct? 问题2a - 以上是否正确?

The code is not correct. 代码不正确。 You're violating The Rule of Three . 你违反了三法则 In particular, the automatically-generated MyDynamicPoint 's copy constructor and assignment operator will make a bitwise copy of the points pointer. 特别是,自动生成的MyDynamicPoint的复制构造函数和赋值运算符将对points指针进行按位复制。 If you copy an instance of MyDynamicPoint , you'll end up with two object sharing the same points pointer: 如果复制MyDynamicPoint的实例, MyDynamicPoint会有两个对象共享相同的points指针:

  • When one of the objects goes of scope, the other becomes unusable. 当其中一个对象超出范围时,另一个对象变得无法使用。
  • When the second object goes out of scope, its destructor will attempt to free memory that's already been freed. 当第二个对象超出范围时,它的析构函数将尝试释放已经释放的内存。 This is undefined behaviour . 这是未定义的行为

Ie Because this new version of the class would contain some dynamically assigned memory, does this mean I have to create a list of pointers to objects, not the objects themselves? 即因为这个新版本的类将包含一些动态分配的内存,这是否意味着我必须创建一个指向对象的指针列表,而不是对象本身?

No, it does not mean that. 不,这并不意味着。 In fact, you should probably continue to store objects by value. 实际上,您应该继续按值存储对象。 However, you do need to fix the rule of three. 但是,您确实需要修复三个规则。

QUESTION 2b - Given that I have just erased the pointer from the list, where do I call delete to deal with the fact there is now dynamic memory to be deleted in the objects? 问题2b - 鉴于我刚刚从列表中删除了指针,我在哪里调用delete来处理现在在对象中删除动态内存的事实? Or does the erase method of stl list call the destructor of the object, taking care of it? 或者stl列表的erase方法是否调用了对象的析构函数,并对其进行处理?

Since you have a list of raw pointers, the destructors will not be called automatically. 由于您有一个原始指针列表,因此不会自动调用析构函数。 The easiest way to fix that is to either store objects by value, or use std::unique_ptr or std::shared_ptr instead of raw pointers. 解决这个问题的最简单方法是按值存储对象,或者使用std::unique_ptrstd::shared_ptr而不是原始指针。

To question 1, there is nothing you need to do. 问题1,你无需做任何事情。 As you store the objects by value the compiler and the library will handle everything. 当您按值存储对象时编译器和库将处理所有内容。

However, when you store pointer as in the second case, you need to delete those pointers that you have allocated with new , or you will have a memory leak. 但是,当你像第二种情况一样存储指针时,你需要delete那些用new分配的指针,否则你会有内存泄漏。

And you have to delete the pointers before doing the erasing, as that can invalidate the iterator: 并且您必须执行擦除之前删除指针,因为这会使迭代器无效:

delete *it;
myList.erase(it);

I think following should work 我认为以下应该有效

MyPoint* ptr = myList.back();

delete ptr;

myList.pop_back();

OR 要么

MyPoint* ptr = myList.back();

delete ptr;

myList.erase(ptr);

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

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