在不使用静态成员的情况下跨对象树共享数据的策略

Posted

技术标签:

【中文标题】在不使用静态成员的情况下跨对象树共享数据的策略【英文标题】:Strategy to sharing data across an object tree without using static members 【发布时间】:2014-08-05 11:48:35 【问题描述】:

我处于需要在多态对象树的许多实例之间共享数据的情况下,但话又说回来,我需要共享数据是“每棵树”,因此在基类中使用静态类成员是不行的真的是一个选择。

我不想用指向共享数据的额外成员指针来“负担”每个实例,所以我目前的方法(考虑到我使用树)是将共享数据作为树根节点的成员,并且每次访问根据访问“树全局”数据的特定节点的深度,共享数据会经过一系列间接链接。

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

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

请注意,静态成员的提及并不将实现范围限制为 C++,我也添加了 C 标记,因为此时我对任何想法持开放态度。

【问题讨论】:

【参考方案1】:

与树节点交互的所有方法都将指向根的指针作为它们的第一个参数。

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

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

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

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 ; 
;

现在我们实际的internal_tree 数据结构不存储指向根的指针。使用它的代码通过flyweight_tree 访问它,该flyweight_tree 存储指向根的指针,但指向根的指针仅存储在访问点,从不长期存储。

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

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

【讨论】:

目标不是隐藏复杂性,而是减少内存占用。为了几个字节的共享数据而保留潜在的数百万个指针太浪费了...... @user3735658 当然可以。所以我减少了内存占用(通过不存储指向根的指针)。相反,每个方法都采用“指向根的指针”。然后,由于传递指向根的指针容易出错且复杂,我们然后使用享元模式降低复杂性。您需要享元模式的示例吗?【参考方案2】:

我能想到的可能性是:

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

【讨论】:

以上是关于在不使用静态成员的情况下跨对象树共享数据的策略的主要内容,如果未能解决你的问题,请参考以下文章

在没有符号链接的情况下跨服务重用数据存储模型

static 关键字的用法

如何在不使用共享首选项的情况下将数据存储为颤动的对象[关闭]

ASP.NET下跨应用共享Session和使用Redis进行Session托管

mfc 类静态成员

Part5 数据的共享与保护 5.3类的静态成员