这个问题的标题非常复杂,所以我将尝试用一个例子来构建它。假设我有一个抽象的基类,有许多继承自它的类。在下面的例子中,我只显示了两个继承的类,但实际上可能会有更多。
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
对象,我首先需要创建一个指向基类的指针数组,然后实际存储对象,我需要创建一个足够大的缓冲区来存储{{1}最大的继承对象的副本,在本例中为N
LargeChild
然后,我可以在Base * children[N];
uint8_t childBuffer[N * sizeof(LargeChild)];
之间children
分发指针,每个指针由childBuffer
分隔。在需要创建对象时,可以使用C ++的“placement new”将它们放置在数组中的指定位置。我需要跟踪sizeof(LargeChild)
中每个对象的类型,以便取消引用childBuffer
中的指针,但这不应该太糟糕。
关于整个设置/实施,我有几个问题:
这是解决问题的好方法,正如我所描述的那样吗?我之前从未实现过这样的任何事情,所以我不知道我是否可以在这里吃午餐,而且有更简单的方法来实现这一目标。
在编译时可以完成多少工作?如果我有children
种类型的继承类(M
,SmallChild
等),但我不知道它们相对于彼此的大小,我可以确定LargeChild
的大小吗?这个大小取决于最大类的大小,但有没有办法在编译时确定这个大小?我可以想象一些预处理器宏迭代遍历类,评估childBuffer
并找到最大值,但我对这种级别的预处理器工作经验很少,并且不知道这会是什么样子。我也可以想象这可能是使用模板,但同样,我没有任何编译时模板巫术的经验,所以我只是基于我的直觉。关于如何实现这一点的任何指示都将受到赞赏。
答案 0 :(得分:3)
你需要能够处理对象吗?如果没有,则覆盖operator new
可能更容易。我指的是:
void* operator new (std::size_t size) throw (std::bad_alloc);
所有覆盖都将从一个大缓冲区分配内存。 size
参数计指定要分配的内存量。
这样你就可以说
children[i] = new SmallChild();
编辑:如果确实需要解除分配,则需要更复杂的数据结构。无论如何,你最终可能会重新实现堆。
答案 1 :(得分:0)
如果对象集是完全静态的(在构建时设置并且在运行时没有改变),通常的方法是使用每个派生类的一组数组并构建&#39; global& #39;指向其他数组的指针数组:
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], ....
如果经常在构建中添加/删除子项,或者子项数组中的顺序很重要,则维护这一点可能很棘手。可能需要使用脚本或构建程序生成上述源文件,该程序读取所需子项的描述。
答案 2 :(得分:0)
考虑到您的约束(即不使用动态分配),您的方法很有趣。
事实上,您正在以自己的方式管理一种union anyChild { smallChild o1; largeChild o2; ... };
数组sizeof(anyChild)
将为您提供所需的最大块大小。
顺便说一句,只要未使用placement new创建所有对象,或者通过显式调用析构函数删除其中一些对象,就可能存在悬挂指针的风险。
答案 3 :(得分:0)
如果将派生类型放入一个并集:
union Child{
SmallChild asSmallChild;
LargeChild asLargeChild;
}
然后,联合将自动具有最大类型的大小。当然,现在您遇到了一个新问题。联合会代表什么类型?您可以在基类中给自己一个提示,也可以改为使Child为包含提示的结构,然后在其中内联联合。例如,在githubs上查看Espressif为ESP32制造的组件,那里有很多很好的联合用法。
无论如何,当您进行分配时,如果分配一个联合类型的数组,它将组成一个最大的子数组...因为这就是联合所做的事情。