【发布时间】:2013-07-22 04:06:46
【问题描述】:
我有许多长链表(它们最多有 20,000 个项目)。它们有不同的起点,但它们最终可以从某个节点开始指向同一个节点。我决定让这样的链表一起成长,共享它们之间的记忆。
这就是我决定用共享指针实现链表的原因:
#include <memory>
struct SharedLinkedList {
int someData;
std::shared_ptr<SharedLinkedList> next;
};
这样一切正常。不再需要的链表被删除。如果他们与其他链表共享某些部分,则仅删除其未共享的部分。
当没有共享部分的较长链表即将被删除时,就会出现问题。删除从第一个元素开始。这减少了对下一个也可以删除的元素的引用数量,并且递归重复直到堆栈溢出。
这是创建长链表然后删除它失败的代码示例。
SharedLinkedList* beginningOfList;
SharedLinkedList* actualElement = new SharedLinkedList();
SharedLinkedList* nextElement;
beginningOfList = actualElement;
for (int i = 0; i < 1000; i++) { // 100 is OK, 1000 is KO
nextElement = new SharedLinkedList();
actualElement->next = std::shared_ptr<SharedLinkedList>(nextElement);
actualElement = nextElement;
}
delete beginningOfList;
我提前感谢以下任何一项:
- shared_pointers 的解释以及我缺少什么。我该如何使用它们?甚至可以使用它们来完成吗?这种内存共享难道不是发明共享指针的目的吗?
- 建议如何重新实现我的代码
- 此代码将用于在我的计算机上运行的科学计算。我可以通过某种方式调整一些东西以获得更大的堆栈吗?
请注意,这个问题不是 c++11 特定的。我不在乎使用了共享指针的哪个实现。我什至实现了我自己的共享指针。这让我有一个更长的链表,但也出现了析构函数中的递归和堆栈溢出。而且我看不出如何在没有析构函数递归的情况下实现共享指针。
编辑:
为了避免混淆:我想重申一下,整个列表都可以共享。所以我们可以称它们为树。
示例如下:
list1 包含:1,2,3,4,5,6,7。
list2 包含:6,6,6,1,2,3,4,5,6,7
list3 包含:10,11,12,1,2,3,4,5,6,7
我想在 3 个 SharedLinkedList 中表示这一点,它们不会通过多次存储 1、2、3、4、5、6、7 来浪费内存,但它们指向同一个地方。这就是需要引用计数的原因。
delete list3; 应该只删除未共享的部分,即元素 10、11、12。
【问题讨论】:
-
据我了解,问题是你的
SharedLinkedList的析构函数的实现。它显然调用第一项的析构函数,然后调用下一项的析构函数,以此类推。您应该能够轻松更改SharedLinkedList析构函数的实现,使其不使用递归函数调用(例如,在列表元素上使用while-loop)。 -
为什么不使用标准的
std::list(或std::vector)std::shared_ptr? -
@jogojapan 看起来
SharedLinkList使用了编译器生成的析构函数。而且您不能真正添加一个遍历所有剩余列表的列表;重点是要对节点进行引用计数。 -
@jamesdlin 好吧,您需要将列表的实现与列表项的实现分开。然后你可以避免递归。 (我的理解是,引用计数是必要的,因为同一个项目可能由多个列表保存,而不是因为它们有助于列表实现本身。如果只是这样,unique-ptr 无论如何就足够了。)
-
@jogojapan 我不确定你是否理解我分享的内容。请查看我所做的修改。
标签: c++ data-structures c++11 recursion shared-ptr