代码之家  ›  专栏  ›  技术社区  ›  Anteru

运算符新重载和对齐

  •  34
  • Anteru  · 技术社区  · 14 年前

    我超载了 operator new ,但我最近遇到了对齐问题。基本上,我有一节课 IBase 它提供 算子新 delete 在所有必需的变体中。所有类都派生自 iBASE 因此也使用自定义分配器。

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

    我可以简单地将所有分配强制为16个字节,在弹出一个32字节对齐的类型之前,这将正常工作。找出里面的路线 算子新 看起来并不简单(我可以在那里做一个虚拟函数调用来获得实际的对齐方式吗?)建议的处理方法是什么?

    我知道 malloc 应该会返回一块内存,该内存适合于所有内容,不幸的是,这个“所有内容”不包括SSE类型,我真的希望在不要求用户记住哪个类型具有哪个对齐方式的情况下让它正常工作。

    3 回复  |  直到 13 年前
        1
  •  21
  •   Johannes Schaub - litb    14 年前

    这是一个可能的解决办法。它将始终选择给定层次结构中对齐度最高的运算符:

    #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字节的要求并继承 :

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

    Alignment<16> Alignment<8> 都暴露了 operator new . 如果你现在说 new B ,编译器将查找 算子新 在里面 会发现 功能:

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

    因此,这将是 模棱两可的 我们将无法编译:这两个都没有隐藏另一个。但如果你现在继承 对齐<16> 实际上来自 对齐<8> 并使 实际上继承他们 算子新 在里面 对齐<8> 将隐藏:

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

    这个特殊的隐藏规则(也称为 显性 规则)但是只有在 全部的 对齐<8> 对象是相同的。因此,我们总是无形地继承:在这种情况下,只有 对齐<8> (或16,…)对象存在于任何给定的类层次结构中。

        2
  •  7
  •   Lance Diduck    14 年前

    mixin是正确的方法,但是重载操作符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没有额外的类大小(除了对齐本身所需的填充之外)。

        3
  •  1
  •   RJVB    13 年前

    使用visualstudioexpress2010,上面的示例似乎不适用于new:

    CacheAlign *c = new CacheAlign;
    

    将给出u align_of(c)==4(我假设是预期的),但cachealign的第一个成员的地址也没有按请求对齐。

    不是最近的问题,但如果我正确理解了op,那么他拥有的类是定义分配器和释放定位器的父类的子类,并且都需要特定的对齐。有一个简单的new调用一些私有分配器来完成实际工作并接收一个对齐参数——父类中具有默认对齐的泛型版本,该版本继承或重载了指定正确对齐的版本,这有什么不对?