C++ 跨对象树共享数据而不使用静态成员的策略

C++ 跨对象树共享数据而不使用静态成员的策略,c++,c,design-patterns,tree,shared-data,C++,C,Design Patterns,Tree,Shared Data,我需要在多态对象树的多个实例之间共享数据,但我需要共享数据为“每棵树”,因此在基类中使用静态类成员并不是一个真正的选项 我不想用指向共享数据的额外成员指针“覆盖”每个实例,因此我当前的方法(考虑到我使用树)是将共享数据作为树根节点的成员,并且每个对共享数据的访问都通过一个间接链,这取决于“树全局”通过的特定节点的深度访问数据 由于在某些情况下,共享数据将被非常频繁地访问(每秒数百万次…至少这是预期的),我想知道是否有一些设计模式可以帮助我避免间接访问根节点,同时仍然不会给对象的足迹带来额外的膨胀

我需要在多态对象树的多个实例之间共享数据,但我需要共享数据为“每棵树”,因此在基类中使用静态类成员并不是一个真正的选项

我不想用指向共享数据的额外成员指针“覆盖”每个实例,因此我当前的方法(考虑到我使用树)是将共享数据作为树根节点的成员,并且每个对共享数据的访问都通过一个间接链,这取决于“树全局”通过的特定节点的深度访问数据

由于在某些情况下,共享数据将被非常频繁地访问(每秒数百万次…至少这是预期的),我想知道是否有一些设计模式可以帮助我避免间接访问根节点,同时仍然不会给对象的足迹带来额外的膨胀

虽然可以将根节点指针“缓存”为本地指针,例如访问共享数据的紧密循环,但在许多情况下,功能将沿树节点级联,甚至在过程中切换树,因此缓存根节点指针仅适用于狭窄的上下文

    注意到静态成员的引用并没有限制实现的范围到C++,我也添加了C标记,因为在这点上我对任何想法都有开放性。
所有与树节点交互的方法都将指向根的指针作为其第一个参数

flyweight包装类可以隐藏此实现细节,您可以通过一个不透明类访问树节点,该不透明类具有一个真正的树节点指针和一个指向根节点的poimter。如果你要一个孩子,它会返回另一个flyweight

现在,树缺少额外的指针,但接口类有额外的指针

如果您的树节点是不可变的(逻辑上),您甚至可以复制它们的状态以减少间接成本

template<class Data>
struct internal_tree {
  Data d;
  std::unique_ptr<internal_tree> left;
  std::unique_ptr<internal_tree> right;
};

template<class Data>
struct flyweight_tree {
private:
  internal_tree* internal = nullptr;
  internal_tree* root = internal;
  flyweight_tree(internal_tree* t, internal_tree* r):internal(t),root(r) {}
public:
  flyweight_tree() {}
  flyweight_tree(internal_tree* r):internal(r) {}
  flyweight_tree left() const { return { internal->left.get(), root }; }
  flyweight_tree right() const { return { internal->right.get(), root }; }
};
模板
结构内部树{
数据d;
std::唯一左上角;
std::唯一的ptr权限;
};
模板
结构flyweight_树{
私人:
内部树*internal=nullptr;
内部树*根=内部;
flyweight_树(内部_树*t,内部_树*r):内部(t),根(r){}
公众:
flyweight_树(){}
flyweight_树(内部_树*r):内部(r){}
flyweight_tree left()常量{return{internal->left.get(),root};}
flyweight_tree right()const{return{internal->right.get(),root};}
};
现在,我们实际的
内部_树
数据结构没有存储指向根的指针。使用它的代码通过flyweight_树访问它,该树存储指向根的指针,但指向根的指针只存储在访问点,而不是长期存储

如果您想要一个
父级
,您甚至可以将其实现为飞锤,其中
飞锤树
存储指向父级的指针的
std::vector
(而不是
)。这在树节点中节省了另一堆内存(没有指向父节点的指针,但是每个使用它的人都可以获得父节点,因为他们通过
flyweight\u树使用它)


我们可以在
internal_树
中实现大部分工作,其中它将
flyweight_树
存储的信息作为参数,或者我们可以在
flyweight_树
中实现大部分树逻辑,并将
internal_树
保留为紧凑的“长期存储”结构。我能想到的可能性有:

  • 如果根的数量有限,则将偏移量(例如在uint8_t中)存储到具有根的静态数组。例如,如果您只有5-10个根,则无需为每个节点存储高达64位的数据
  • 为什么不在自己的进程中运行每个“树”(在操作系统级别)?这样,每个根目录都可以存储为静态数据并全局访问
  • 如果可以绑定节点数,可能可以使用特殊的分配器:首先在给定内存边界的开头分配根,例如0x0010000、0x002000等。。。然后通过对每个节点进行简单的减法/位移位来检索根的地址?但您必须能够确保可以在每个树的内存区域内分配所有节点

目标不是隐藏复杂性,而是减少内存占用。为了几个字节的共享数据而保留数百万个指针实在是太大的浪费了…@user3735658当然。因此,我减少了内存占用(通过不存储指向根的指针)。相反,每个方法都采用“指向根的指针”。然后,由于将指针传递给根容易出错且复杂,所以我们使用flyweight模式降低了复杂性。你需要一个飞锤模式的例子吗?