在编译/运行时检查特定结构/类没有任何虚函数的方法是什么。执行贴装新时,需要进行此检查以确保正确的字节对齐。
拥有一个虚拟函数可以将整个数据移动一个 vtable 指针大小,这将与放置新操作符一起完全搞乱。
更多细节:我需要适用于所有主要编译器和平台的内容,例如: VS2005,VC ++ 10,GCC 4.5和Sun Studio 12.1在Windows,Linux和Solaris之上。
使用以下方案确保可以正常工作的东西应该足够了:
struct A { char c; void m(); };
struct B : A { void m(); };
如果有人决定进行此更改:
struct A { char c; virtual void m(); };
struct B : A { void m(); };
看到struct A must not contain virtual functions.
答案 0 :(得分: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);
};
这个小帮助器提供了正确的对齐作为编译时常量(因此适用于模板编程)。它可能大于所需的最小对齐(*)。
通常情况下,使用placement new可能没问题,除非您实际在“原始缓冲区”上使用它。在这种情况下,缓冲区的大小应使用以下公式确定:
// C++03
char buffer[sizeof(T) + alignof(T) - 1];
或者您应该使用C ++ 0x工具:
// C++0x
std::aligned_storage<sizeof(T), alignof(T)> buffer;
确保虚拟表“正确”对齐的另一个技巧是使用联合:
// C++03 and C++0x
union { char raw[sizeof(T)]; void* aligner; } buffer;
aligner
参数保证buffer
正确地与指针对齐,因此对于虚拟表指针也是如此。
编辑:@Tony建议的其他解释。
(*)这是如何工作的?
要理解它,我们需要深入研究类的内存表示。类的每个子元素都有自己的对齐要求,例如:
struct A { int a; char b; int c; };
+----+-+---+----+
| a |b|xxx| c |
+----+-+---+----+
其中xxx
表示添加了填充,以便c
适当对齐。
A
的对齐方式是什么?一般来说,它是子元素的更严格的对齐,所以在这里,int
的对齐(通常4
,因为int
通常是32
位积分。
为了“猜测”任意类型的对齐,我们因此使用AlignHelper
模板“欺骗”编译器。请记住,sizeof(AlignHelper<T>)
必须是对齐的倍数,因为类型应该在数组中连续布局,因此我们希望我们的类型将在c
属性之后填充,并且对齐将是c
(根据定义为1
)加上填充的大小。
// AlignHelper<T>
+----------------+-+---+
| t |c|xxx|
+----------------+-+---+
// T
+----------------+
| t |
+----------------+
当我们sizeof(AlignHelper<T>) - sizeof(T)
时,我们会发现这种差异。但令人惊讶的是,它可能是0
。
问题来自这样一个事实:如果在T
的末尾有一些填充(未使用的字节),那么智能编译器可以决定在那里隐藏c
,从而缩小大小将是0
。
c
属性的大小(使用char数组),直到我们最终获得非零差异。在这种情况下,我们会得到一个“紧密”对齐,但最简单的方法是拯救并使用sizeof(T)
,因为我们已经知道它是对齐的倍数。
最后,无法保证我们使用此方法获得的对齐是T
的对齐,我们得到它的倍数,但它可能更大,因为sizeof
是依赖于实现的例如,编译器可以决定将所有类型与2个边界的幂对齐。
答案 1 :(得分:3)
什么是一个体面的方法 在编译/运行时检查a 特定的struct / class没有 任何虚拟功能
template<typename T>
struct Is_Polymorphic
{
struct Test : T { virtual ~Test() = 0; };
static const bool value = (sizeof(T) == sizeof(Test));
};
以上class
可以帮助您检查编译时given class is polymorphic or not 是否。 [注意:virtual
继承也包含vtable]
答案 2 :(得分:2)
dynamic_cast
仅允许polymorphic classes
,因此您可以将其用于编译时检查。
答案 3 :(得分:1)
使用tr1中的is_pod类型特征?
答案 4 :(得分:1)
你几乎肯定做错了什么。
但是,鉴于您决定做错事,您不想知道您的tpe是否没有虚函数。您想知道将类型视为字节数组是否可以。
在C ++ 03中,你的类型是POD吗?幸运的是,有一个特点,恰如其分地命名为is_pod<T>
。这是由C ++ 03中的Boost / TR1提供的,尽管它需要一个相对现代的编译器[gcc&gt; 4.3,MSVC&gt; 8,其他我不知道]。
在C ++ 11中,您可以通过询问您的类型是否可以轻松复制来简化您的要求。同样,有一个特点:is_trivially_copyable<T>
。
在任何一种情况下,也有is_polymorphic<T>
,但正如我所说的,无论如何,这真的不是你想要的。如果您使用的是较旧的编译器,如果从Boost获得它,它确实具有开箱即用的优势;它会执行其他地方提到的sizeof
测试,而不是像false
那样只为所有用户定义的类型报告is_pod
。
无论如何,你最好120%确定你的构造函数是noop;这不是可以验证的东西。
我刚看到你的编辑。在您列出的内容中,Sun Studio是唯一可能没有必要的内在函数来使用这些特性的工具。 gcc和MSVC已经有好几年了。
答案 5 :(得分:0)
您无法确定某个类是否具有虚函数。