在编译/运行时检查特定结构/类没有任何虚函数的方法是什么.在进行新的放置时,需要进行此检查以确保正确的字节对齐.
拥有一个虚拟函数就可以将整个数据移动一个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.
有一些工具和技巧(取决于你使用的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个边界的幂, 例如.
几乎可以肯定你做错了什么。
然而,考虑到您已经决定做错事,您不想知道您的 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其他地方提到的测试,而不是false像is_pod.
无论如何,你最好 120% 确定你的构造函数是一个 noop;这不是可以验证的事情。
我刚刚看到你的编辑。在您列出的产品中,Sun Studio 是唯一可能不具备这些特征发挥作用所需的内在功能的产品。gcc 和 MSVC 已经拥有它们好几年了。
| 归档时间: |
|
| 查看次数: |
746 次 |
| 最近记录: |