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 list的erase方法调用对象的析构函数来处理它?
提前感谢您的帮助,
,
Adam
当你有一个类的数据成员具有自动存储持续时间(即它们的生命周期与这个类的实例绑定),像这样:
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::vector
或std::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
的实例,最终会得到两个对象共享同一个points
指针:
- 当一个对象超出作用域时,另一个对象变得不可用。
- 当第二个对象超出作用域时,它的析构函数将尝试释放已经释放的内存。这是未定义行为。
。因为这个新版本的类将包含一些动态分配的内存,这是否意味着我必须创建一个指向对象的指针列表,而不是对象本身?
不,这不是那个意思。实际上,您可能应该继续按值存储对象。但是,您确实需要修改"三"规则。
问题2b -假设我刚刚从列表中删除了指针,我在哪里调用delete来处理对象中现在有动态内存要删除的事实?还是stl list的erase方法调用对象的析构函数来处理它?
由于您有一个原始指针列表,因此不会自动调用析构函数。解决这个问题的最简单方法是按值存储对象,或者使用std::unique_ptr
或std::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);
- 我使用向量来创建类对象列表.初始化向量时如何使用参数调用构造函数?
- 如何在不销毁对象的情况下返回对象列表
- 如何使用Q_PROPERTY公开自定义对象列表
- 什么更好?返回对象指针列表?或返回指向对象列表的指针?
- c++ 循环访问对象列表并删除对象
- 循环访问对象列表 c++
- Cython正在获取C++对象列表的长度
- 如何在屏幕上显示对象列表?
- Strcmp 类行为应用于C++中的对象列表
- 如何迭代指针指向的对象列表?
- C++对象列表数组
- 使用BOOST :: PYTHON返回裸露的类对象列表,这些对象是不可复制的
- 返回对象列表
- C 链接对象列表
- 使用for_each从对象列表中调用打印功能
- 从对象列表中获取具有特定成员值的所有对象
- 按指针列出的 C++ 对象列表
- 子类对象列表重新解释为基类对象列表?(C++11).
- 在C++中的混合对象列表中使用相同的方法
- Python:我们应该如何编写一个函数来返回对象/列表和布尔标志