问题描述
我正在超载operator new
,但是最近遇到对齐问题.基本上,我有一个类IBase
,它在所有必需的变体中提供了operator new
和delete
.所有类都从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>
并使A
和B
虚拟继承它们,则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.
这篇关于运算符新的重载和对齐的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!