我做了一个可爱的泛型(即模板) List类来处理C++中的列表。原因是我发现std::list类在日常使用中非常难看,而且因为我经常使用lists,所以我需要一个新的。主要的改进是,在我的类中,我可以使用[]从其中获取项。此外,还需要实现一个IComparer系统来对事物进行排序。
我在OBJLoader中使用这个List类,我的类加载Wavefront .obj文件并将它们转换为网格。OBJLoader包含指向以下“类型”的指针列表: 3D位置、3D法线、uv纹理坐标、顶点、面和网格。顶点列表中的对象必须链接到所有3D位置、3D法线和uv纹理坐标列表中的某些对象。面链接到顶点,网格链接到面。所以它们都是相互关联的。
为了简单起见,让我们考虑一下,在某些上下文中,只有两个指针列表:List<Person*>和List<Place*>。Person类包含字段List<Place*> placesVisited,Place类包含字段List<Person*> peopleThatVisited。所以我们有这样的结构:
class Person
{
...
public:
Place* placeVisited;
...
};
class Place
{
...
public:
List<People*> peopleThatVisited;
};现在我们有了以下代码:
Person* psn1 = new Person();
Person* psn2 = new Person();
Place* plc1 = new Place();
Place* plc2 = new Place();
Place* plc2 = new Place();
// make some links between them here:
psn1->placesVisited.Add(plc1, plc2);
psn2->placesVisited.Add(plc2, plc3);
// add the links to the places as well
plc1->peopleThatVisited.Add(psn1);
plc2->peopleThatVisited.Add(psn1, psn2);
plc3->peopleThatVisited.Add(plc3);
// to make things worse:
List<Person*> allThePeopleAvailable;
allThePeopleAvailable.Add(psn1);
allThePeopleAvailable.Add(psn2);
List<Place*> allThePlacesAvailable;
allThePlacesAvailable.Add(plc1);
allThePlacesAvailable.Add(plc2);
allThePlacesAvailable.Add(plc3);全都做完了。当我们到达}时会发生什么?所有dtor都会被调用,程序会崩溃,因为它会尝试删除两次或更多次内容。
我的列表中的dtor如下所示:
~List(void)
{
cursor = begin;
cursorPos = 0;
while(cursorPos < capacity - 1)
{
cursor = cursor->next;
cursorPos++;
delete cursor->prev;
}
delete cursor;
}其中,Elem是:
struct Elem
{
public:
Elem* prev;
T value;
Elem* next;
};T是泛型List类型。
这让我们回到了这个问题:有什么方法可以安全地删除我的List类?其中的元素可能是指针,也可能不是指针,如果它们是指针,我希望在删除List时能够指定是删除其中的元素,还是只删除它们周围的Elem包装器。
智能指针可能是一个答案,但这意味着我不能拥有List<bubuType*>,而只能拥有List<smart_pointer_to_bubuType>。这可能是可以的,但是再说一次:声明List<bubuType*>不会导致错误或警告,在某些情况下,智能指针会导致实现中的一些问题:例如,我可能想为一些WinAPI返回声明一个List<PSTR>。我认为将这些PSTR放入智能指针中将是一项丑陋的工作。因此,我正在寻找的解决方案我认为应该以某种方式与List模板的释放系统相关。
有什么想法吗?
发布于 2011-03-11 02:42:42
最好的答案是,您必须考虑每个对象的生命周期,以及管理该生命周期的责任。
特别是在人与所到之处的关系中,很可能两者都不应该自然地对他人的生命负责:人们可以独立于所到之处生活,地方是存在的,无论他们是否被访问过。这似乎暗示了人员和站点的生命周期都与其他人无关,并且所持有的指针与资源管理无关,而是引用(不是C++意义上的)。
一旦知道谁负责管理资源,就应该删除(或者最好将资源保存在容器中或适当的智能指针中,如果需要动态分配),并且必须确保在引用相同元素的其他对象完成删除之前,不会发生删除。
如果在一天结束时,您的设计所有权不明确,您可以退回到使用shared_ptr ( boost或std),小心不要创建会导致内存泄漏的循环依赖项。同样,要正确使用shared_ptr,您必须回去思考,考虑对象的生命周期……
发布于 2011-03-11 02:50:48
如果您负责释放内存,请始终使用智能指针。不要使用原始指针,除非你知道你不负责删除那个内存。对于WinAPI返回值,将它们包装到智能指针中。当然,原始指针列表并不是错误的,因为您可能希望拥有一个不属于其内存的对象列表。但是避免智能指针肯定不是任何问题的解决方案,因为它们是一个完全必要的工具。
只需使用标准列表。这就是它的作用。
发布于 2011-03-11 03:07:24
在下面的代码行: //在它们之间建立一些链接: psn1->placesVisited.Add(plc1,plc2);psn2->placesVisited.Add(plc2,plc3);
//也添加地点链接plc1->peopleThatVisited.Add(psn1);plc2->peopleThatVisited.Add(psn1,psn2);plc3->peopleThatVisited.Add(plc3);
堆上的实例包含彼此的指针。不仅是一个,而且将相同的位置指针添加到多个人也会导致问题(在内存中多次删除相同的对象)。
告诉你学习STL或使用shared_ptr (Boost)可能是一个很好的建议,然而,正如David Rodríguez所说,你需要考虑对象的生命周期。换句话说,您需要重新设计此场景,以便对象之间不包含指向彼此的指针。
例子:真的有必要使用指针吗?-在这种情况下,如果你需要STL列表或向量,你需要使用shared_ptr,但同样,如果对象相互引用,即使是最好的shared_ptr实现也不会实现它。
如果需要位置和人员之间的这种关系,则设计一个类或使用一个容器来承载彼此的引用,而不是让人员和位置彼此指向。就像RDBMS中的多对多表一样。然后,您将拥有一个类/容器,它将负责在过程结束时删除指针。这样,Places和Person之间的关系将不存在,只存在于容器中。
致敬,J. Rivero
https://stackoverflow.com/questions/5264046
复制相似问题