操作员新的重载和对齐

时间:2010-03-02 21:23:22

标签: c++ operator-overloading new-operator

我正在超载operator new,但我最近遇到了对齐的问题。基本上,我有一个课程IBase,可以在所有必需的变体中提供operator newdelete。所有类都派生自IBase,因此也使用自定义分配器。

我现在面临的问题是我有一个孩子Foo,它必须是16字节对齐,而其他所有孩子在对齐到8字节时都很好。然而,我的内存分配器只在默认情况下与8字节边界对齐,所以现在IBase::operator new中的代码返回一块不可用的内存。这应该如何正确解决?

我可以简单地将所有分配强制为16个字节,这将正常工作,直到弹出32字节对齐的类型。找出operator new内的对齐似乎并不简单(我可以在那里进行虚函数调用以获得实际的对齐吗?)推荐的处理方法是什么?

我知道malloc应该返回一段适合所有内容的内存,不幸的是,这个“一切”都不包含SSE类型,我真的很想让这个工作不需要用户要记住哪种类型具有哪种对齐方式。

2 个答案:

答案 0 :(得分:21)

这是一种可能的解决方案。它将始终选择给定层次结构中具有最高对齐的运算符:

#include <exception>
#include <iostream>
#include <cstdlib>

// provides operators for any alignment >= 4 bytes
template<int Alignment>
struct DeAllocator;

template<int Alignment>
struct DeAllocator : virtual DeAllocator<Alignment/2> {
  void *operator new(size_t s) throw (std::bad_alloc) {
    std::cerr << "alignment: " << Alignment << "\n";
    return ::operator new(s);
  }

  void operator delete(void *p) {
    ::operator delete(p);
  }
};

template<>
struct DeAllocator<2> { };

// ........... Test .............
// different classes needing different alignments
struct Align8 : virtual DeAllocator<8> { };
struct Align16 : Align8, virtual DeAllocator<16> { };
struct DontCare : Align16, virtual DeAllocator<4> { };

int main() {
  delete new Align8;   // alignment: 8
  delete new Align16;  // alignment: 16
  delete new DontCare; // alignment: 16
}

它基于支配规则:如果查找中存在歧义,并且在派生类和虚拟基类的名称之间存在歧义,则会改为派生类的名称。


DeAllocator<I>继承DeAllocator<I / 2>的原因引发了疑问。答案是因为在给定的层次结构中,类可能存在不同的对齐要求。设想IBase没有对齐要求,A有8字节要求,B有16字节要求并继承A

class IBAse { };
class A : IBase, Alignment<8> { };
class B : A, Alignment<16> { };

Alignment<16>Alignment<8>都会公开operator new。如果您现在说new B,编译器将在operator new中查找B并找到两个函数:

            // op new
            Alignment<8>      IBase
                 ^            /
                  \         /
                    \     /
 // op new            \ /
 Alignment<16>         A
            \         /
              \     /
                \ /
                 B 

B ->      Alignment<16>  -> operator new
B -> A -> Alignment<8> -> operator new

因此,这将是不明确的,我们将无法编译:这些都不会隐藏另一个。但是,如果您现在虚拟地从Alignment<16>继承Alignment<8>并使AB虚拟地继承它们,operator new中的Alignment<8>将被隐藏:

            // op new
            Alignment<8>      IBase
                 ^            /
                / \         /
              /     \     /
 // op new  /         \ /
 Alignment<16>         A
            \         /
              \     /
                \ /
                 B 

此特殊隐藏规则(也称为支配规则)仅适用于所有 Alignment<8>对象相同的情况。因此,我们总是虚拟地继承:在这种情况下,任何给定的类层次结构中只存在一个 Alignment<8>(或16,...)对象。

答案 1 :(得分:7)

mixins是正确的方法,但是重载operator new不是。这将实现您的需求:

__declspec(align(256))  struct cachealign{};
__declspec(align(4096)) struct pagealign{};
struct DefaultAlign{};
struct CacheAlign:private cachealign{};
struct PageAlign: CacheAlign,private pagealign{};

void foo(){
 DefaultAlign d;
 CacheAlign c;
 PageAlign p;
 std::cout<<"Alignment of d "<<__alignof(d)<<std::endl;
 std::cout<<"Alignment of c "<<__alignof(c)<<std::endl;
 std::cout<<"Alignment of p "<<__alignof(p)<<std::endl;
}

打印

Alignment of d 1
Alignment of c 256
Alignment of p 4096

对于gcc,请使用

struct cachealign{}__attribute__ ((aligned (256)));

请注意,可以自动选择最大的对齐方式,这适用于放置在堆栈上的对象,新的对象以及其他类的成员。它也没有添加任何虚拟和假设EBCO,没有额外的大小(在对齐本身所需的填充之外)。