我最近看到一个代码库,我担心它违反了对齐约束。我已经对其进行了清理以生成一个最小的示例,如下所示。简而言之,球员是:
游泳池。对于“高效”的某些定义,这是一个有效分配内存的类。池保证返回与请求大小对齐的一块内存。
对象列表。此类存储对象的同类集合。一旦对象的数量超过某个阈值,它就会将其内部表示从列表更改为树。Obj_list的大小是一个指针(在 64 位平台上为 8 个字节)。它的商店当然会超过这个。
聚合。此类代表系统中非常常见的对象。它的历史可以追溯到早期的 32 位工作站时代,它被“优化”(在同一个 32 位时代)以使用尽可能少的空间。Aggregate可以为空,也可以管理任意数量的对象。
在此示例中,聚合项始终从Pool分配,因此它们始终对齐。此示例中Obj_list的唯一出现是Aggregate对象中的“隐藏”成员,因此它们总是使用Placement new分配。以下是支持类:
class Pool
{
public:
Pool();
virtual ~Pool();
void *allocate(size_t size);
static Pool *default_pool(); // returns a global pool
};
class Obj_list
{
public:
inline void *operator new(size_t s, void * p) { return p; }
Obj_list(const Args *args);
// when constructed, Obj_list will allocate representation_p, which
// can take up much more space.
~Obj_list();
private:
Obj_list_store *representation_p;
};
这里是聚合。请注意成员声明member_list_store_d:
// Aggregate is derived from Lesser, which is twelve bytes in size
class Aggregate : public Lesser
{
public:
inline void *operator new(size_t s) {
return Pool::default_pool->allocate(s);
}
inline void *operator new(size_t s, Pool *h) {
return h->allocate(s);
}
public:
Aggregate(const Args *args = NULL);
virtual ~Aggregate() {};
inline const Obj_list *member_list_store_p() const;
protected:
char member_list_store_d[sizeof(Obj_list)];
};
我最关心的是那个数据成员。这是初始化和访问的伪代码:
Aggregate::Aggregate(const Args *args)
{
if (args) {
new (static_cast<void *>(member_list_store_d)) Obj_list(args);
}
else {
zero_out(member_list_store_d);
}
}
inline const Obj_list *Aggregate::member_list_store_p() const
{
return initialized(member_list_store_d) ? (Obj_list *) &member_list_store_d : 0;
}
您可能会建议我们将 char 数组替换为指向Obj_list类型的指针,初始化为 NULL 或类的实例。这给出了正确的语义,但只是改变了内存成本。如果内存仍然很宝贵(它可能是,这是一种 EDA 数据库表示),在Aggregate对象确实有成员的情况下,用指向Obj_list的指针替换 char 数组将花费更多的指针。
除此之外,我真的不想分心这里的主要问题,即对齐。我认为上述构造是有问题的,但在标准中找不到比对 'system/library' new的对齐行为的一些模糊讨论更多的内容。
那么,上述构造除了导致偶尔的管道停顿之外还有什么作用吗?
编辑:我意识到有一些方法可以使用嵌入式 char 数组替换该方法。最初的建筑师也是如此。他们丢弃了它们,因为内存非常宝贵。现在,如果我有理由接触该代码,我可能会更改它。
然而,我的问题是,关于这种方法固有的对齐问题,我希望人们能解决这个问题。谢谢!