在C ++中调用缓慢的递归析构函数

时间:2012-02-12 18:55:29

标签: 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而不会导致内存泄漏?

3 个答案:

答案 0 :(得分:2)

如果你在破坏期间处理数百万个对象和堆栈溢出,那么你可能想要研究的是在某些类型的容器中存储指向所有节点对象的指针,这些容器不允许重复值并支持迭代,例如std::setstd::unordered_set。然后在图形节点本身中,仅存储指向连接的图形节点的指针。在删除图形时,不要递归每个图形节点的指针以删除节点...而是简单地转到包含指向整个图形中节点集的指针的容器,并迭代通过容器,逐个销毁每个节点。不需要递归,因为您不需要“跟踪”每个图形节点的指针集来销毁图形节点所连接的所有节点。您也不必担心std::shared_ptr

的空间和时间开销

答案 1 :(得分:2)

如果您不单独删除节点,则可能需要考虑使用memory pool而不是引用计数。内存池可以让你以极快的速度吹掉你的整个图形,代价是(取决于池的类型)可能无法恢复单个已删除对象使用的内存,直到整个图形被销毁。

答案 2 :(得分:2)

  

是否有可能以某种方式避免在不导致内存泄漏的情况下声明析构函数虚拟?

我不敢,更确切地说,在指针的动态类型与指针的静态类型不匹配的指针上调用delete p时,你无法避免未定义的行为。

  

但仍然很慢

我担心的是它的样子。删除数百万个对象需要一些时间。你可以绕过它,例如。通过避免完全破坏单个物体,例如。将它们全部放入某个池中(并将其作为一个整体丢弃)。取决于你的破坏需求。