操作员新的重载和对齐

Ant*_*eru 34 c++ operator-overloading new-operator

我正在超载operator new,但我最近遇到了对齐的问题.基本上,我有一个类IBase,提供operator newdelete所有必要的变种.所有类都派生自IBase并因此也使用自定义分配器.

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

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

我知道malloc应该返回一块适合所有内容的内存,不幸的是,这个"所有内容"不包含SSE类型,我真的很想让这个工作无需用户记住哪种类型有哪个对齐.

Joh*_*itb 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
}
Run Code Online (Sandbox Code Playgroud)

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


为什么DeAllocator<I>继承问题上升了DeAllocator<I / 2>.答案是因为在给定的层次结构中,类可能存在不同的对齐要求.想象一下,IBase它没有对齐要求,A有8字节要求,并且B有16字节要求并继承A:

class IBAse { };
class A : IBase, Alignment<8> { };
class B : A, Alignment<16> { };
Run Code Online (Sandbox Code Playgroud)

Alignment<16>并且Alignment<8>都曝光了operator new.如果你现在说new B,编译器会寻找operator newB和会发现2个功能:

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

B ->      Alignment<16>  -> operator new
B -> A -> Alignment<8> -> operator new
Run Code Online (Sandbox Code Playgroud)

因此,这将是模糊的,我们将无法编译:这些都不会隐藏另一个.但是,如果您现在Alignment<16>虚拟地继承并实际Alignment<8>创建AB继承它们,那么operator newin Alignment<8>将被隐藏:

            // op new
            Alignment<8>      IBase
                 ^            /
                / \         /
              /     \     /
 // op new  /         \ /
 Alignment<16>         A
            \         /
              \     /
                \ /
                 B 
Run Code Online (Sandbox Code Playgroud)

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


Lan*_*uck 7

mixins是正确的方法,但重载运算符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;
}
Run Code Online (Sandbox Code Playgroud)

打印

Alignment of d 1
Alignment of c 256
Alignment of p 4096
Run Code Online (Sandbox Code Playgroud)

对于gcc,请使用

struct cachealign{}__attribute__ ((aligned (256)));
Run Code Online (Sandbox Code Playgroud)

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