C++中的慢速递归析构函数调用
Slow recursive destructor call in C++
我使用C++来表示使用引用计数对象的大型图。类似于:
// Public class
class Pointer{
public:
// Constructors
Pointer(...){ node = ...; node->counter++;}
// Copy constructor
Pointer(Pointer& const other){ node = other.node; node->counter++;}
// Destructor
~Point(){ if(--node->counter==0) delete node;}
// Other methods
...
Node* node;
};
// Node base class
class Node{
// Constructor
Node():counter(0){}
// Destructor
virtual ~Node(){}
// public:
unsigned long counter;
};
// Node derived class
class NodeDerived : public Node{
// Constructors and other methods
...
// Destructor
virtual ~NodeDerived(){ ... }
// Children
Pointer children[2];
};
因此存在一个公共类Pointer
,它包含一个指向多态类Node的指针。从Node派生的某些类将包含新的Pointer
实例。注意,几个Pointer
实例可以指向同一个Node
实例。
我使用这个类来构建具有数百万个实例的非常大的图,这很好。当要删除图形时,问题就出现了。对于默认的析构函数实现,这会导致堆栈溢出,因为在根节点的析构因子中,调用子节点的析构函数,在子节点的析构函数中,调用孩子节点的子节点的解构函数,依此类推
我通过为上面的NodeDerived
类实现一个析构函数来解决这个问题,该析构函数通过使用堆栈来避免递归调用。这是可行的,但仍然非常缓慢,我正在寻找一种加快速度的方法。是否可以在不导致内存泄漏的情况下避免声明析构函数virtual
?
如果在销毁过程中处理数百万个对象和堆栈溢出,那么您可能需要研究的是将指向所有节点对象的指针存储在某种类型的容器中,该容器不允许重复值并支持迭代,如std::set
或std::unordered_set
。然后在图节点本身中,只存储指向连接的图节点的指针。在删除图时,不要为了删除节点而遍历每个图节点的指针。。。相反,只需转到包含指向整个图中节点集的指针的容器,然后遍历容器,逐个销毁每个节点。不需要递归,因为你不需要"跟随"每个图节点的指针集来破坏图节点连接的所有节点。你也不需要担心std::shared_ptr
的空间和时间开销。
如果不单独删除节点,您可能需要考虑使用内存池而不是引用计数。内存池可以让你非常快地毁掉整个图形,代价是(取决于池的类型)在整个图形被破坏之前,可能无法恢复单个被删除对象使用的内存。
是否可以在不导致内存泄漏的情况下以某种方式避免声明析构函数为虚拟的?
恐怕不是,更确切地说,在指针的动态类型和静态类型不匹配的指针上调用delete p
时,您无法避免未定义的行为。
但是仍然很慢
恐怕就是这样。删除数百万个对象需要一些时间。你可以绕过它,例如避免完全破坏单个物体,例如将它们全部放在某个水池中(并将其作为一个整体丢弃)。取决于你的破坏需求。
- 通过递归进行因子分解
- 什么时候调用组成单元对象的析构函数
- 递归导致程序崩溃,但 while 循环中的相同概念不是来自C++析构函数
- 递归列表:调用析构函数的所有内容
- 这个递归类需要一个自定义析构函数?
- 链表的递归析构函数
- C++二进制树递归析构函数问题
- 是否可以在容器之类的堆栈中创建非递归析构函数
- 析构函数、图和递归性
- 是否应始终在析构函数中清除/归零成员数据
- 二叉树析构函数的递归调用
- 二叉树的递归析构函数
- C++中的慢速递归析构函数调用
- 指向"raw"资源的指针是否应该在析构函数中归零?
- C++中的递归析构函数
- 使用堆栈的非递归析构函数二叉搜索树
- 用栈实现二叉搜索树的非递归析构
- 如何在递归选择排序算法中调用类析构函数
- 是链表、树等的递归析构函数
- 二进制搜索树递归析构函数