【问题标题】:Generic Quadtree通用四叉树
【发布时间】:2013-09-30 10:32:03
【问题描述】:

我正在编写一个四叉树类作为图形库的一部分,我正面临一个设计问题。 一个主要目标是允许库的用户使用他们自己的节点类型轻松扩展四叉树。每个节点都有一个指向其四个子节点中的第一个的指针。在拆分父节点时,我使用原型模式四次“克隆”父节点(库不知道其真实类型)。所以这里是 Node 类:

class CNode {
  public:
    virtual CNode* clone();

  protected:
    CNode* pChilds;
}

库的用户现在可以定义自己的节点并添加遍历方法:

class MyNode : public CNode {
  public:
    virtual CNode* clone() {
      return new MyNode;
    }

    void myTraverse() {
      if(pChilds[0] != nullptr)
        static_cast<MyNode*>(pChilds[0])->traverse();
    }
}

可以看出,我必须从基类转换到派生类。或者,我可以制作所有与四叉树相关的类模板,但我真的不想这样做。 我也不能使用升压。除了 boost:: 任何类似的解决方案,使用 RTTI 或动态转换都会变慢,因为四叉树是性能关键组件,必须尽可能快地运行!

在增加一些类型安全性的同时,是否有可能保持 static_cast 的速度? (四叉树只会包含单一类型的节点)。

【问题讨论】:

  • 我已经标记了你的问题 [C++]。如果这不正确,请随时恢复并添加不同的语言标签。
  • 并澄清您的问题:myTraverse 成员函数是如何被调用的?它是由图书馆调用的吗?如果是这样,库是如何知道它的,因为它没有在基类中定义?
  • 澄清:“myTraverse”是从已知 MyNode 的地方调用的,所以这不是问题

标签: c++ casting containers quadtree


【解决方案1】:

我知道你说过你不想使用模板,但是这种事情正是模板的用途。通过使你的节点类成为一个虚拟类,你在每次构造和销毁时都会产生额外的开销,并且将节点结构的大小扩大至少一个指针,这将降低缓存的一致性。

此外,拒绝使用模板会导致您陷入static_casts 和不安全代码的泥潭。请注意,例如,如果pChilds 指向MyNode 的数组并且MyNode 有任何成员变量,那么下标运算符将无形地无法正常工作。

【讨论】:

  • 嗯,也许你是对的。我的自定义节点类声明看起来像这样? “类 MyNode:公共 CNode {...}”
  • 是的,很有可能。容器节点是 CRTP 的主要用途之一。
【解决方案2】:

既然你说

四叉树只会包含单一类型的节点

那么您可以使用该假设来优化您的代码。如,您可以在MyNode::myTraverse() 的正文中假设this 的孩子都将成为MyNode,您可以安全地将static_cast 任何孩子MyNodes。

但是,您可能会担心如果代码中的错误违反了数据结构的不变量,即它只能包含一种类型的元素,会发生什么情况。这就是条件编译可以派上用场的地方。假设符号 DEBUG 在调试版本中定义:

#ifdef DEBUG
#define my_cast dynamic_cast
#else
#define my_cast static_cast
#endif

...
void MyNode::myTraverse() {
  if(pChilds[0] != nullptr)
    my_cast<MyNode*>(pChilds[0])->traverse();
}

这将在您的发布版本中为您提供static_cast 的速度,并在您的调试版本中提供dynamic_cast 的运行时类型检查,其中速度不是问题。而且很有可能,如果您在发布版本中违反了结构的不变量,您也将在调试版本中这样做,并且您的调试版本可能会崩溃(它实际上是未定义的行为,但大多数平台都会崩溃当您取消引用空指针时)具有访问冲突异常/段错误。

或者,您可以暂时坚持使用dynamic_cast,并在准备好发布和/或完成测试后切换到static_cast,这表明使用dynamic_cast而不是static_cast导致不可接受的性能损失。

编辑:由于您正在创建一个库,请确保您的用户非常清楚clone() 必须返回一个与调用它的类型相同的对象,并且一些例子。这是您无法确保其他程序员不会犯错误的情况之一,您只需要相信他们可以阅读 cmets 或文档。

【讨论】:

  • 这是个好主意,谢谢。我想我会坚持下去。
  • 您还没有讨论节点生命周期,但您可能还需要一个公共虚拟析构函数......
  • 你说得对,我只是省略了不在问题范围内的所有内容。我已经有了节点生命周期管理的解决方案。
猜你喜欢
  • 2017-08-09
  • 1970-01-01
  • 2013-05-12
  • 2014-06-14
  • 2015-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多