这是一个可能的解决方案。它将始终选择给定层次结构中具有最高对齐的运算符:
#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字节要求并继承A:
class IBAse { };
class A : IBase, Alignment<8> { };
class B : A, Alignment<16> { };
Alignment<16> 和 Alignment<8> 都公开了一个 operator new。如果您现在说new B,编译器将在B 中查找operator new,并会找到两个 函数:
// 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 将被隐藏:
// op new
Alignment<8> IBase
^ /
/ \ /
/ \ /
// op new / \ /
Alignment<16> A
\ /
\ /
\ /
B
这种特殊的隐藏规则(也称为 dominance 规则)仅在 all Alignment<8> 对象相同时才有效。因此,我们总是虚拟继承:在这种情况下,任何给定的类层次结构中都只有 一个 Alignment<8>(或 16 个,...)对象。