比较C++中的虚拟函数和C中的虚拟表,编译器(以及足够大的项目)在去虚拟化方面做得很好吗?
天真地说,似乎C++中的虚拟函数具有更多的语义,因此可能更容易去虚拟化。
更新: Mooing提到内联去虚拟化功能。快速检查显示没有使用虚拟表进行优化:
struct vtab {
int (*f)();
};
struct obj {
struct vtab *vtab;
int data;
};
int f()
{
return 5;
}
int main()
{
struct vtab vtab = {f};
struct obj obj = {&vtab, 10};
printf("%d\n", obj.vtab->f());
}我的GCC将不会内联f,虽然它被直接称为,即去虚拟化。C++中的等价物
class A
{
public:
virtual int f() = 0;
};
class B
{
public:
int f() {return 5;}
};
int main()
{
B b;
printf("%d\n", b.f());
}即使是内联f,C和C++之间也有第一个不同之处,尽管我认为在C++版本中添加的语义在这种情况下并不相关。
更新2:为了在C中去虚拟化,编译器必须证明虚拟表中的函数指针有一定的值。为了在C++中进行去虚拟化,编译器必须证明对象是特定类的实例。在第一种情况下,证据似乎更难证明。但是,虚拟表通常只在很少的地方被修改,最重要的是:仅仅因为它看起来更难,并不意味着编译器在这方面没有那么好(否则,您可能会认为xoring通常比添加两个整数更快)。
发布于 2011-08-13 10:31:39
不同之处在于,在C++中,编译器可以保证虚拟表地址不会改变。在C中,它只是另一个指针,你可以用它造成任何形式的破坏。
然而,
通常只在很少的地方修改虚拟表。
编译器不知道在C中,在C++中,它可以假设它永远不会改变。
发布于 2014-01-29 16:14:14
我试图在http://hubicka.blogspot.ca/2014/01/devirtualization-in-c-part-2-low-level.html中总结一下为什么一般的优化很难去虚拟化。您的测试用GCC 4.8.1为我准备,但在稍微不那么简单的测试案例中,如果您将指向“对象”的指针从主目录中传递出来,则不会。
原因是为了证明obj中的虚拟表指针和虚拟表本身并没有改变别名分析模块,必须跟踪您可以指向它的所有可能的位置。在一个非平凡的代码中,在当前编译单元之外传递东西,这通常是一个失败的游戏。
C++为您提供了更多关于对象类型何时更改以及何时已知的信息。GCC利用它,它将在下一个版本中更多地使用它。(我很快也会写信的)。
发布于 2011-08-12 23:08:07
是的,如果编译器可以推断虚拟化类型的确切类型,那么它可以“去虚拟化”(甚至内联!)那个电话。编译器只能保证无论发生什么,这都是所需的函数,才能做到这一点。
主要关注的是线程处理。在C++示例中,即使在线程环境中,这些保证仍然有效。在C中,这是不能保证的,因为对象可以被另一个线程/进程抓取,并被覆盖(不管是有意的还是故意的),因此函数永远不会被“去虚拟化”或直接调用。在C中,查找将始终在那里。
struct A {
virtual void func() {std::cout << "A";};
}
struct B : A {
virtual void func() {std::cout << "B";}
}
int main() {
B b;
b.func(); //this will inline in optimized builds.
}https://stackoverflow.com/questions/7046739
复制相似问题