访问对象而不在每个实例中存储指向它的指针

时间:2016-11-28 02:00:27

标签: c++ c++11

我的情况类似于以下

struct Cell
{
  uint16_t x, y;
  // other fields

  Cell* right();
  Cell* above();
  ...
}

class Container
{
private:
  uint16_t width, height;
  Cell* data;

public:
  Container(uint16_t width, uint16_t height) : 
    width(width), height(height), data(new Cell[width*height]) { }

  Cell* cellAt(uint16_t x, uint16_t y) { &data[x*height + y]; }
};

Container* container;

Cell* Cell::right() { return container->cellAt(x+1, y); }
...

为了显示设计,我修剪了大部分代码(例如范围检查等等)。

基本上,这允许我在代码中的任何地方访问相邻单元而不直接传递Container对象(这将产生更多冗长的代码,并且邻居在代码库周围访问数百次)。该解决方案运行良好,甚至不需要在代码周围知道Container类,因为Cell就足够了。

这种方法有一个很大的限制:它只允许Container的一个实例。现在我发现自己处于需要Container的多个实例的情况,具有独立的Cell数组,但我不想更改代码的结构。

我正在考虑一种允许多个Container但是

的智能方法
  • 我想保留结构
  • 我绝对不想在每个Container*内存储Cell以避免浪费大量内存(我们谈论的是数百万个单元格)
  • 我希望保持尽可能高效,因为这些邻居函数被大量使用

我在考虑分配width*height + 1 Cell个实例并使用第一个实例在其内存中存储Container*,但问题是如何计算第一个Cell的地址总宽度/高度是Container本身的字段(Cell不知道)。

所以我想我应该为每列存储至少一个指向Container*的指针(它们按cellAt函数显示的列存储,其次要原因对问题无用)。这会浪费每个height*sizeof(Cell)的{​​{1}}字节,这可能相当多,但我猜单指针是没办法的。

基本上我可以做类似的事情:

Container

然后检索对象。当然,这是一个肮脏的黑客行为,可能会给架构带来问题,如果Container** container = reinterpret_cast<Container**>(&data[0]); *container = this; 不支持未对齐的访问。

我是否缺少更智能的解决方案?

3 个答案:

答案 0 :(得分:1)

我有3个解决方案。

细胞知道它们位于连续的2d缓冲区中。

在较低维度中查找第一个元素很容易。所以现在你在(N,0)。如果N0,我们就完成了,我们找到了2d数组的开头。

之前的元素是(N-1,Last),其中Last+1是较低维度的大小。现在您可以跳转到(0,0)

或者,从单元格中删除x和y,替换为容器指针。从this和容器指针的地址动态计算x和y。

如果我们想要认真对待,我们会删除所有多余的信息。

杀死x和y。写一个存储Cell*Container*的单元格视图类型。通过此视图调解与Cell的所有互动。它计算xy并知道容器大小。它只能将Container*指针传递给Cell的每个方法。

CellView然后替换代码库中的Cell*。您甚至可以覆盖->以返回this并保持大部分用途不变。

CellView cellAt(uint16_t x, uint16_t y) { return {&data[x*height + y], this}; }

struct Cell{
//  uint16_t x, y;
  // other fields

  Cell* right(Container*);
  Cell* above(Container*);
  ...
};

struct CellView{
  // maybe: uint16_t x, y;
  Cell* cell;
  Container* container;


  CellView right()const{ return {cell->right(container), container}; };
  CellView above()const{ return {cell->above(container), container}; };
  ...
};

基本上将状态移动到“指针”并移出单元格。

答案 1 :(得分:0)

考虑到您所说的限制,以下是两种可能的解决方案:

1)对于需要Container完成工作的所有函数,只需要将Container作为参数。

例如:

Cell* Cell::right(Container* container)
{
    return container->cellAt(x+1, y);
}

2)不要询问Cell有关Container知道的事情,而是向Container询问其Cell。换句话说,将rightabove和类似函数移动到Container。

例如:

Cell* Container::nextRightCell(Cell* from)
{
    return cellAt(from->x+1, from->y);
}

答案 2 :(得分:0)

使用全局Container数组并使用CellContainer上的多态来获得模板整数参数,以存储Container的索引...

我不认为这是一个好主意(Cell成为一个纯粹的虚拟抽象结构)但是,只是为了好玩...

#include <cstdint>
#include <iostream>

struct Cell
 {
   uint16_t x, y;
   // other fields

   virtual Cell* right() = 0;
   //Cell* above();
 };

class Container
 {
   private:
      uint16_t width, height;
      Cell* data;

   public:
      Container(uint16_t w0, uint16_t h0, Cell * d0)
         : width(w0), height(h0), data(d0)
       { }

      Cell* cellAt(uint16_t x, uint16_t y)
       { return &data[x*height + y]; }
 };

Container * containers[10];

template <std::size_t I>
struct CellI : public Cell
 {
   Cell* right()
    { std::cout << I << std::endl; return containers[I]->cellAt(x+1, y); }
 };

template <std::size_t I>
class ContainerI : public Container
 {
   public:
      ContainerI (uint16_t w0, uint16_t h0)
         : Container(w0, h0, new CellI<I>[w0*h0])
       { }
 };


int main()
 {
   containers[0] = new ContainerI<0>(10, 20);
   containers[1] = new ContainerI<1>(20, 40);
   containers[2] = new ContainerI<2>(30, 60);
   // ...

   containers[0]->cellAt(5,5)->right(); // print 0
   containers[1]->cellAt(5,5)->right(); // print 1
   containers[2]->cellAt(5,5)->right(); // print 2
 }