由于放置新用法而禁止虚拟功能的体面方式

Nic*_*ick 9 c++

在编译/运行时检查特定结构/类没有任何虚函数的方法是什么.在进行新的放置时,需要进行此检查以确保正确的字节对齐.

拥有一个虚拟函数就可以将整个数据移动一个vtable指针大小,这将与放置new运算符一起完全搞乱.


更多细节:我需要适用于所有主要编译器和平台的东西,例如Windows,Linux和Solaris之上的VS2005,VC++ 10,GCC 4.5和Sun Studio 12.1.

保证与以下场景一起使用的东西应该足够了:

struct A { char c; void m(); };
struct B : A { void m(); };
Run Code Online (Sandbox Code Playgroud)

如果有人决定进行此更改:

struct A { char c; virtual void m(); };
struct B : A { void m(); };
Run Code Online (Sandbox Code Playgroud)

很高兴看到编译时错误 struct A must not contain virtual functions.

Mat*_* M. 7

有一些工具和技巧(取决于你使用的C++的版本)来获得一个类的正确对齐.

在C++ 0x中,alignof命令类似sizeof但返回所需的对齐.

在C++ 03中,首先要注意的是大小是对齐的倍数,因为元素在数组中需要是连续的.这意味着使用大小作为对齐是过度热心(并可能浪费空间)但工作正常.通过一些技巧,您可以获得更好的价值:

template <typename T>
struct AlignHelper
{
  T t;
  char c;
};

template <typename T>
struct Alignment
{
  static size_t const diff = sizeof(AlignHelper<T>) - sizeof(T);
  static size_t const value = (diff != 0) ? diff : sizeof(T);
};
Run Code Online (Sandbox Code Playgroud)

这个小帮助器给出了正确的对齐作为编译时常量(因此适用于模板编程).它可能大于所需的最小对齐(*).

通常情况下,使用placement new应该没问题,除非你实际上在"原始缓冲区"上使用它.在这种情况下,缓冲区的大小应使用以下公式确定:

// C++03
char buffer[sizeof(T) + alignof(T) - 1];
Run Code Online (Sandbox Code Playgroud)

或者您应该使用C++ 0x工具:

// C++0x
std::aligned_storage<sizeof(T), alignof(T)> buffer;
Run Code Online (Sandbox Code Playgroud)

确保虚拟表"正确"对齐的另一个技巧是使用联合:

// C++03 and C++0x
union { char raw[sizeof(T)]; void* aligner; } buffer;
Run Code Online (Sandbox Code Playgroud)

aligner参数保证buffer了指针的正确对齐,因此对于虚拟表指针也是如此.

编辑:@Tony建议的其他解释.

(*) 这是如何运作的 ?

要理解它,我们需要深入研究类的内存表示.类的每个子元素都有自己的对齐要求,例如:

struct A { int a; char b; int c; };

+----+-+---+----+
| a  |b|xxx| c  |
+----+-+---+----+
Run Code Online (Sandbox Code Playgroud)

其中xxx表示添加的填充物,以便c适当地对齐.

对齐是A什么?一般来说,它是子元素的严格比对,所以在这里,对齐int(这往往是4因为int往往是一个32比特组成).

为了"猜测"任意类型的对齐,我们因此使用AlignHelper模板"欺骗"编译器.请记住,sizeof(AlignHelper<T>)必须是对齐的倍数,因为类型应该在数组中连续布局,因此我们希望我们的类型将在c属性之后填充,并且对齐将是c(1根据定义)的大小加上填充的大小.

// AlignHelper<T>
+----------------+-+---+
|        t       |c|xxx|
+----------------+-+---+

// T
+----------------+
|        t       |
+----------------+
Run Code Online (Sandbox Code Playgroud)

当我们这样做时,sizeof(AlignHelper<T>) - sizeof(T)我们得到了这个差 令人惊讶的是,它可能是0.

问题来自这样一个事实:如果在末尾有一些填充(未使用的字节)T,那么智能编译器可以决定存储c在那里,因此大小的差异就是0.

显然,我们可以尝试递归地增加c属性的大小(使用char数组),直到我们最终获得非零差异.在这种情况下,我们会得到一个"紧密"对齐,但最简单的方法是拯救和使用sizeof(T),因为我们已经知道它是对齐的倍数.

最后,无法保证我们使用此方法得到的对齐是对齐T,我们得到它的倍数,但它可能更大,因为sizeof依赖于实现,编译器可以决定将所有类型对齐2个边界的幂, 例如.


Den*_*ose 2

几乎可以肯定你做错了什么。

然而,考虑到您已经决定做错事,您不想知道您的 tpe 是否没有虚拟函数。您想知道是否可以将您的类型视为字节数组。

在C++03中,你的类型是POD吗?幸运的是,有一个特征,恰如其分地命名为is_pod<T>。这是由 C++03 中的 Boost/TR1 提供的,尽管它需要一个相对现代的编译器 [gcc > 4.3,MSVC > 8,其他我不知道]。

在 C++11 中,您可以通过询问您的类型是否可以轻松复制来缓解您的要求。再说一次,有一个特点:is_trivially_copyable<T>

无论哪种情况,都存在is_polymorphic<T>,但正如我所说,无论如何这都不是您想要的。如果您使用的是较旧的编译器,如果您从 Boost 获取它,它确实具有开箱即用的优点;它执行sizeof其他地方提到的测试,而不是falseis_pod.

无论如何,你最好 120% 确定你的构造函数是一个 noop;这不是可以验证的事情。


我刚刚看到你的编辑。在您列出的产品中,Sun Studio 是唯一可能不具备这些特征发挥作用所需的内在功能的产品。gcc 和 MSVC 已经拥有它们好几年了。