【问题标题】:Data model, cyclic references数据模型,循环引用
【发布时间】:2023-03-04 14:52:01
【问题描述】:

我有以下用于存储经络和纬线的数据结构。

每个制图点存储:
A] 地理空间坐标、制图扭曲等
B] 指向北/南/东/西节点的指针。

它允许存储点之间的关系,首先是它们与 经线/平行...

 class Node2DCart 
 { 
     protected: 
             //Coordinates of the point 
             double lat; 
             double lon; 
             double lattrans; 
             double lontrans; 
             double x; 
             double y; 
 ..... 
             //Pointers to adjacent points in geographic network 
             Node2DCart *left; 
             Node2DCart *right; 
             Node2DCart *top; 
             Node2DCart *bottom; 
 ..... 
 }; 

子午线的数据结构存储子午线的经度,开始 经线的点和终点以及点数。

 class Meridian 
 { 
     private: 
             unsigned int points_count; 
             double longitude; 
             Node2DCart *start; 
             Node2DCart *end; 
 .... 
 }; 

所有点都存储在节点列表中:

typedef std::vector<Node2DCart*> TNodes2DCartList; 

class Node2DCartList 
{ 
     protected: 

             TNodes2DCartList nodes; 

     ... 
}; 

但是为 Node2DList 编写复制构造函数有一个大问题。 Meridian/Parallel 和 Node2Dlist 之间存在循环依赖关系。

复制构造函数使用std::map 并用新的替换旧点和链接,这不是实现问题...但是从类 Meridian 开始/结束的指针指向旧 Node2DList 中的点... Node2DList 复制构造函数应该必须通知所有指向旧 Node2DList 点的经络,并将所有指针更改为新 Node2DList 点。此模型不允许。

可能的解决方案是添加两个指向子午线和平行点所属的指针:

 class Node2DCart 
 { 
     protected: 
             //Coordinates of the point 
             double lat; 
             double lon; 
             double lattrans; 
             double lontrans; 
             double x; 
             double y; 
 ..... 
             //Pointers to adjacent points in geographic network 
             Node2DCart *left; 
             Node2DCart *right; 
             Node2DCart *top; 
             Node2DCart *bottom; 
 ..... 
             Meridian *m;
             Parallel *p;
 };

恐怕这个提议的模型不好。两个班级之间仍然有自行车参考......有人可以帮我改进它吗?谢谢...

【问题讨论】:

  • 当您说Node2DList 时,您是指TNodes2DCartListNode2DCartList 还是完全其他的意思?

标签: c++ copy-constructor datamodel cyclic-reference


【解决方案1】:

在我看来,在某些情况下用索引替换指针的解决方案会很慢。

  • 如果我们不删除任何点,point_id 将表示他点的索引,例如。节点[poin_id],没关系。

  • 但是如果我们删除列表中的任何点,point_id 将不会代表它在列表中的索引。我们将不得不使用 std::find 并获取找到的点的 point_id。这大大降低了代码的速度……

  • 如果我们删除任何点,我们可以重新索引点列表以避免上述问题。但是我们不必忘记重新索引所有经络开始/结束索引......但这需要一些时间并且与问题中的复制构造函数相同。而且我认为,通过另一个类使用复制构造函数来影响某个类的数据,代表数据结构的提议不是很合适......

【讨论】:

    【解决方案2】:

    有人可以帮我改进吗?

    在这种情况下,我通常会采用这样的方法:

     typedef int node_id_t;
     enum { NODE_NULL = 0 };
     // or enum node_id_t { NODE_NULL=0 }; for strict typing.
    
     class Node2DCart 
     { 
         protected:
                 node_id_t id;    // id of the node
                 //Coordinates of the point 
                 double lat; 
                 double lon; 
                 double lattrans; 
                 double lontrans; 
                 double x; 
                 double y; 
     ..... 
                 //Pointers to adjacent points in geographic network 
                 node_id_t left;
                 node_id_t right; 
                 node_id_t top; 
                 node_id_t bottom; 
     ..... 
     };
    
     class Meridian
     {
         private:
                 unsigned int points_count;
                 double longitude;
                 node_id_t start;
                 node_id_t end;
     .... 
     };
    
     /* ... */
    
     std::vector<Node2DCart *> node_registry;
    
     // during initialization:
     node_registry.push_back( NULL );
     // to reserve 0th element to denote the NULL pointer
    
     Node2DCart *
     GetNode(node_id_t id)
     {
        // placeholder of the id range check
        return node_registry[id];
     };
    
     node_id_t
     AddNode(Node2DCart *n)
     {
        node_registry.push_back(n);
        return node_id_t(node_registry.size()-1);
     };
    

    然后使用数字 node_id_t 代替 Node2DCart *。还可以输入std::set(或std::map,在AddNode() 中更新/测试)以确保所有Node2DCart 对象都是唯一的,并且如果不重用现有对象的ID。

    本质上是一个寻址方案,为每个节点提供一个唯一的全局标识符。不是最好/最简单的解决方案,使用全局容器,但不止一次帮助了我。特别是为了避免内存泄漏并确保彻底销毁相互依赖的对象的整个层次结构。

    除了typedef int node_id_t,还可以使用struct node_id_t { int id; }; 和重载转换运算符来简化节点ID 查找。

    【讨论】:

    • +1 表示寻址方案,这就是我在类似情况下使用的。关于node_id_t,我只能推荐Boost.StrongTypedef:它创建了一个具有旧类型所有特征的新类型,但它不能隐式转换。非常方便。
    猜你喜欢
    • 2015-10-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多