今天我遇到了一个非常棘手的bug,与corruption有关。如果我当时留意到警告并查找相关信息,应该不会这么难以发现它,但是由于我没能找到为什么会出现这个特定警告的相关信息,所以我就放任不管了,这是一个错误。
这是Visual Studio 2013给我的警告:
warning C4316: object allocated on the heap may not be aligned 16
当通过const引用将align(16)的临时变量传递给构造函数时,会生成以下代码所示的结果:
class Vector
{};
__declspec(align(16)) class VectorA
{};
class Shape
{
public:
Shape(const Vector& vec) {}
};
class ShapeA
{
public:
ShapeA(const VectorA& vec) : mVec(vec) {}
private:
VectorA mVec;
};
int main(int argc, char *argv[])
{
Shape* shape = new Shape(Vector()); // ok
ShapeA* shapea = new ShapeA(VectorA()); // warning C4316:
// object allocated on the heap may not be aligned 16
}
这引发了一些问题:
由于我不负责所述结构的对齐(它来自库),因此我无法真正解决这个问题。有什么推荐的解决方法吗?
"作为警告,我猜它不会有什么剧烈的影响。但我发现的影响非常严重而又沉默(只有在将完全无关的浮点数引用传递给sqlite函数时才出现)"。(编辑:实际上这是错误的,堆没有因为这个而被破坏)这真的就像:"这段代码肯定会导致堆破坏,请勿执行。"那样简单吗?还是说有更复杂的行动链来导致这样的情况?
我想到的一个解决方法是使用对齐对象的unique_ptr而不是直接作为成员,但我有点不愿意在以前只有简单结构体的地方添加unique_ptr(即使它允许我对我的代码进行pimpl)。
补充说明
事实证明,我正在子类化提供新的和删除重载以确保正确对齐的VectorA类,但我的自己的子类没有。但下一段中出现的第二种情况更难摆脱这个警告:
扩展版
导致警告的另一种情况是:
__declspec(align(16)) class VectorA
{
void* operator new(size_t size)
{
void* p = _aligned_malloc(size, 16);
if(p == 0) throw std::bad_alloc();
return p;
}
void operator delete(void *p)
{
VectorA* pc = static_cast<VectorA*>(p);
_aligned_free(p);
}
};
class ShapeB
{
public:
ShapeB() {}
private:
VectorA mVec;
};
int main()
{
std::unique_ptr<BoxShapeB> shapeb = std::make_unique<BoxShapeB>();
}
似乎我的任何一个类如果有像这个例子中的对齐成员,实例化时也会产生警告。就像我上面所说的,消除警告的方法是使用指向这些成员的指针。 在之前的 Visual Studio 版本中,使用完全相同的代码时没有出现这个警告,所以我的问题是:以上内容到什么程度是明显错误的,是否应该尽量避免?即这个警告应该如何处理?
假设我的大多数类都有一个共同的基类 Object,并且大多数类都有 align(16) Vector 作为成员,那么将重载的 new 和 delete 放在这个基类中,忘记将所有的成员 Vectors 转换为指针,是否值得一试?这种方法可行吗?