对于所有具有对齐成员的类,出现警告C4316: 对齐。

6

今天我遇到了一个非常棘手的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
}

这引发了一些问题:

  1. 由于我不负责所述结构的对齐(它来自库),因此我无法真正解决这个问题。有什么推荐的解决方法吗?

  2. "作为警告,我猜它不会有什么剧烈的影响。但我发现的影响非常严重而又沉默(只有在将完全无关的浮点数引用传递给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 转换为指针,是否值得一试?这种方法可行吗?
2个回答

2
这个问题与您所建议的通过const引用传递对齐结构体无关。请参阅C4316的文档。问题在于您声明了对齐结构,但没有提供适当的new/delete运算符来处理这种对齐。
如果您在使用第三方预构建库时遇到这种情况,则应向其作者报告请求以解决此问题。如果您可以自己构建此库,则可以添加上述运算符。

是的,你说得对,这并不是因为库没有提供new/delete,而是因为我对它进行了子类化,并没有在我的子类中提供它们。但第二个问题仍然存在,所以我会重新表述一下。 - MONK
在任何其他类中将对齐类作为成员会产生警告(这与第一种情况相反,即使定义了新的删除重载以确保对齐)。 - MONK
@MONK 我认为第二个问题与第一个问题相似。在第一个问题中,您继承了对齐的结构并需要正确的运算符。我猜测第二种情况也是如此。但这只是一个猜测。 - Bogdan
那么你认为,创建一个名为Aligned16的基类,并在需要时进行子类化以避免在所有类中膨胀这些重载,这是否是一个可行的解决方案? 编辑:哎呀,我刚意识到这不起作用,因为运算符需要在实际类中。所以我只能选择一边使用指针,或者在另一边将所有类都膨胀为重载运算符。 - MONK

1
经过一番 Dr. Memory 的摸索和整体错误解决后,发现:
  1. 堆并不由于对齐问题而被破坏。这意味着我仍然不知道这个警告的程度应该多么令人担忧,但是我选择了一种解决方案来解决它:

  2. 为了避免这个警告,我使用了以下解决方案,目前让我满意:创建一个名为 Align16 的类,重载所有的 new 和 delete 操作符以使用 _aligned_malloc 和 _aligned_free。然后,每个包含对齐成员的类都必须从 Align16 继承,以消除警告。
    我知道这不是可移植的,因为涉及到 _aligned_malloc,所以如果需要,我还可以退回到 Bullet Physics 自定义实现的内存分配器中。


你尝试过修改项目的C++代码生成参数中的结构成员对齐参数吗?你可以将整个项目的对齐参数设置为16字节。 - Peter R

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接