本文介绍了运算符新的重载和对齐的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm overloading operator new, but I recently hit a problem with alignment. Basically, I have a class IBase which provides operator new and delete in all required variants. All classes derive from IBase and hence also use the custom allocators.

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

The problem I'm facing now is that I have a child Foo which has to be 16-byte aligned, while all others are fine when aligned to 8-byte. My memory allocator however aligns to 8-byte boundaries only by default, so now the code in IBase::operator new returns an unusable piece of memory. How is this supposed to be solved correctly?

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

I can simply force all allocations to 16 bytes, which will work fine until a 32-byte aligned type pops up. Figuring out the alignment inside operator new doesn't seem to be trivial (can I do a virtual function call there to obtain the actual alignment?) What's the recommended way to handle this?

我知道malloc应该返回一块适合所有情况的内存,不幸的是,此一切"都不包括SSE类型,我真的很想在不要求用户进行操作的情况下使它工作记住哪种类型具有哪种对齐方式.

I know malloc is supposed to return a piece of memory which is suitably aligned for everything, unfortunately, this "everything" doesn't include SSE types and I'd really like to get this working without requiring the user to remember which type has which alignment.

推荐答案

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

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.

有人提出了为什么DeAllocator<I>继承DeAllocator<I / 2>的问题.答案是因为在给定的层次结构中,类可能存在不同的对齐要求.假设IBase没有对齐要求,A有8个字节的要求,而B有16个字节的要求,并且继承了A:

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>Alignment<8>都公开了operator new.如果现在说new B,则编译器将在B中查找operator new并找到两个函数:

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

因此,这将是模糊的,我们将无法编译:这两个都没有隐藏另一个.但是,如果您现在从Alignment<8>虚拟继承Alignment<16>并使AB虚拟继承它们,则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

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

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.

这篇关于运算符新的重载和对齐的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-12 00:28