目前,我有一个多态容器,可以在继承层次结构中存储任何类型。为此,我管理自己的缓冲区。
我想支持任何可以添加的未来类型,因此我不想为可以添加到容器中的类型设置静态大小。
在我的缓冲区中,由于较小的类型可能存储在较大的类型之前,因此当我不得不移除较小的元素然后移动较大的元素时会发生覆盖。
注意:对于以下示例,缓冲区中的所有类型都从相同的基类继承。
考虑这个内存布局包含一个字节对象 A (字节0)后跟一个2字节对象 B (字节2和3):
// initial state
[ a ][ b ][ b ]
// remove a
[ x ][ b ][ b ]
// x is the position to shift to, capital B is the overwritten byte
[ b ][ B ][ ]
我想要删除 A ,所以我销毁了相关对象(这意味着a
现在可供我使用),然后我想移动构造 B < / strong>从a
开始。显然,当我尝试写字节1时会有覆盖,因为字节1已被 B 本身占用。
我试图在分配阶段解决这个问题,通过跟踪分配的前一个元素的大小,我可以保留足够的填充字节来防止这种情况。有了这个新策略,内存布局将如下所示:
// initial state, when I added B, padding bytes = sizeof( B ) - sizeof( last ) = 1
[ a ][ - ][ b ][ b ]
// remove a, we can clearly see that there's enough space for B now
[ x ][ - ][ b ][ b ]
// safe to move B to position x
[ b ][ b ][ ][ ]
如果要添加的对象大于前一个对象,我只添加填充字节。
但是,即使在修复之后,擦除元素仍然可能导致问题发生:
// initial state: object a is 1 byte, object b is 1 byte, object c is 4 bytes
[ a ][ - ][ - ][ c ][ c ][ c ][ b ][ - ][ - ][ c ][ c ][ c ]
// erase first c, occupying bytes 1 through 5
[ a ][ b ][ - ][ - ][ c ][ c ][ c ][ ][ ][ ][ ][ ]
// erase b
[ a ][ x ][ - ][ - ][ c ][ c ][ c ][ ][ ][ ][ ][ ]
// shift c - since c > a, required padding = c - a = 4 - 1 = 3
[ a ][ - ][ - ][ - ][ x ][ c ][ c ][ c ][ ][ ][ ][ ]
// when attempting to shift C, there will be an overwrite at the capital C bytes
[ a ][ - ][ - ][ - ][ c ][ C ][ C ][ ][ ][ ][ ][ ]
这是一个产生覆盖问题的简单程序:
#include <iostream>
#include <new>
#include <cstdint>
struct Base
{
virtual ~Base() = default;
virtual void print() const = 0;
};
struct A : public virtual Base /* 16 byte */
{
A( char x ) : x_{ x } {}
virtual void print() const { std::cout << x_ << '\n'; }
char x_;
};
struct B : public virtual Base /* 24 bytes */
{
B( long double x ) : x_{ x } {}
virtual void print() const{ std::cout << x_ << '\n'; }
long double x_;
};
int main()
{
unsigned char data[ sizeof( A ) + sizeof( B ) ];
// these are stored as void*, used polymorphically in the actual code
A* pa = ::new ( &data ) A{ 1 };
B* pb = ::new ( &data + sizeof( A ) ) B{ 2 };
pa->print();
pb->print();
// I want to remove the first A from my list
pa->~A();
// shift following element back, overwrite due to its larger size
pb = ::new ( &data ) B{ *pb };
}
我考虑过的可能解决方案: