2014-09-02 88 views
2

这个问题的标题是相当复杂的,所以我会尝试用一个例子对它进行框架。假设我有一个抽象基类,有许多继承自它的类。在下面的例子中,我只展示了两个继承的类,但实际上可能会有更多。静态分配继承对象数组

class Base { 
public: 
    Base(); 
    virtual ~Base() = 0; 
/// Other methods/members 
}; 

class SmallChild: public Base { 
public: 
    SmallChild(); 
    ~SmallChild(); 
/// Other methods/members such that sizeof(SmallChild) < sizeof(LargeChild) 
}; 

class LargeChild : public Base { 
public: 
    LargeChild(); 
    ~LargeChild(); 
/// Other methods/members such that sizeof(LargeChild) > sizeof(SmallChild) 
}; 

我需要实现一个容器,它最多可以存储N继承的对象。这些对象需要在运行时创建/销毁并放置在容器中,但由于项目中的约束(特别是在嵌入式硬件上),动态内存分配不是一种选择。容器需要静态分配所有空间。另外,编译器不支持C++ 11。

只有一种方法可以实现。为了引用N对象,我首先需要创建一个指向基类的指针数组,然后实际存储对象,我需要创建一个足够大的缓冲区来存储最大继承对象的副本,在这种情况下是LargeChild

Base * children[N]; 
uint8_t childBuffer[N * sizeof(LargeChild)]; 

我可以随后在children分配指针横跨childBuffer,每个都由sizeof(LargeChild)分离。由于需要创建对象,因此可以使用C++的“placement new”将它们放置在数组中的指定位置。我需要跟踪childBuffer中每个对象的类型,以取消引用children中的指针,但这不应该太糟糕。

我有一个关于这个整个设置/执行几个问题:

  1. 这是一个很好的方法为我所描述它解决问题?我从来没有像以前那样执行过任何操作,所以我不知道我是否在这里吃午饭,还有更简单的方法来完成此任务。

  2. 这可以在编译时完成多少?如果我有M类型的继承类(SmallChild,LargeChild等)但我不知道知道它们的大小相互关系,我怎么能确定大小childBuffer?这个大小取决于最大类的大小,但是有没有办法在编译时确定这个大小?我可以想象一些预处理器宏遍历类,评估sizeof并找到最大值,但是我对这个级别的预处理器工作很少有经验,不知道这会是什么样子。我也可以想象使用模板可以做到这一点,但是我再也没有关于编译时模板魔法的经验,所以我只是基于我的直觉。任何方向如何实施这将不胜感激。

+1

这有帮助吗? http://stackoverflow.com/questions/354442/looking-for-c-stl-like-vector-class-but-using-stack-storage – PaulMcKenzie 2014-09-02 17:27:29

+0

如果你有一个派生类型的列表,那么宏+预处理器可以工作与那个名单找到最大的类型。将所有派生类型的列表放在一个地方是否可行?另外,多大(约)是最大的? – 2014-09-02 17:43:05

+0

第一次擦除后是否会插入插入物,还是混在一起? – 2014-09-02 17:44:18

回答

3

您是否需要处理对象?否则,可能会更容易覆盖operator new。我指的是:

void* operator new (std::size_t size) throw (std::bad_alloc); 

所有的覆盖都会从一个大的缓冲区分配内存。 size参数表指定分配多少内存。

这样,你应该能够只是说

children[i] = new SmallChild(); 

编辑:如果你确实需要解除分配,则需要更复杂的数据结构。无论如何,您最终可能会重新实施堆。

+0

好吧,这比我想象的要容易得多。即使“重新实现堆”,如果他知道最大尺寸和最大计数,那么“重新实现堆”可能相当麻烦。 – 2014-09-02 17:49:45

+0

对于“模拟堆”,可能每个类型都有一个缓冲区(一个char [...]类型的静态变量,然后你得到一个分配的数字和单个链表的头部空闲。分配的数量反映了bufer作为块数组的使用情况,一旦耗尽,我们使用为我们创建的列表,因为我们将其释放。 – Arkadiy 2014-09-02 18:17:45

0

如果这组对象是完全静态的(在构建时设置,并且在运行时不会改变),通常的做法是使用每个派生类的一组数组,并使用指针构建'global'数组到其他阵列:

static SmallChild small_children[] = { 
    { ...initializer for first small child... }, 
    { ...initializer for second small child... }, 
    ... 
}; 
static LargeChild large_children[] = { 
    { ...initializer for first large child... }, 
    ... 
}; 
Base *children[N] = { &small_children[0], &small_children[1], &large_children[0], .... 

如果有被添加的儿童这可以是棘手的,以保持/从构建除去频繁,或者如果儿童数组中的顺序是重要的。可能需要使用脚本或生成程序来生成上述源文件,该程序可以读取所需孩子的描述。

0

考虑到您的限制(即不使用动态分配),您的方法很有趣。

事实上,你正在用自己的方式管理一组union anyChild { smallChild o1; largeChild o2; ... };sizeof(anyChild)会给你最大的块大小,你正在寻找。

顺便说一句,只要所有对象没有用新的位置创建,或者通过显式调用它们的析构函数删除了某些对象,那么可能存在悬挂指针的风险。