operator new overloading and alignment

前端 未结 2 613
情深已故
情深已故 2020-12-12 21:08

I\'m overloading operator new, but I recently hit a problem with alignment. Basically, I have a class IBase which provides operator new

相关标签:
2条回答
  • 2020-12-12 21:19

    mixins are the right approach, however overloading operator new is not. This will accomplish what you need:

    __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;
    }
    

    Prints

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

    For gcc, use

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

    Note that there is automatic selection of the largest alignment, and this works for objects placed on the stack, ones that are new'd, and as members of other classes. Nor does it add any virtuals and assuming EBCO, no extra size to the class (outside of the padding needed for the alignment itself).

    0 讨论(0)
  • 2020-12-12 21:46

    This is a possible solution. It will always choose the operator with the highest alignment in a given hierarchy:

    #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
    }
    

    It's based on the dominance rule: If there is an ambiguity in lookup, and the ambiguity is between names of a derived and a virtual base class, the name of the derived class is taken instead.


    Questions were risen why DeAllocator<I> inherits DeAllocator<I / 2>. The answer is because in a given hierarchy, there may be different alignment requirements imposed by classes. Imagine that IBase has no alignment requirements, A has 8 byte requirement and B has 16 byte requirement and inherits A:

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

    Alignment<16> and Alignment<8> both expose an operator new. If you now say new B, the compiler will look for operator new in B and will find two functions:

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

    Thus, this would be ambiguous and we would fail to compile: Neither of these hide the other one. But if you now inherit Alignment<16> virtually from Alignment<8> and make A and B inherit them virtually, the operator new in Alignment<8> will be hidden:

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

    This special hiding rule (also called dominance rule) however only works if all Alignment<8> objects are the same. Thus we always inherit virtually: In that case, there is only one Alignment<8> (or 16, ...) object existing in any given class hierarchy.

    0 讨论(0)
提交回复
热议问题