cco*_*com 15 c c++ compiler-optimization
比较C++中的虚函数和C中的虚拟表,一般编译器(以及足够大的项目)在虚拟化方面做得很好吗?
天真地看来,C++中的虚函数似乎有更多的语义,因此可能更容易虚拟化.
更新: Mooing Duck提到了内联的虚拟化功能.快速检查显示虚拟表的错过优化:
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());
}
Run Code Online (Sandbox Code Playgroud)
我的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());
}
Run Code Online (Sandbox Code Playgroud)
甚至内联f.所以C和C++之间存在第一个区别,尽管我不认为C++版本中添加的语义在这种情况下是相关的.
更新2:为了在C中进行虚拟化,编译器必须证明虚拟表中的函数指针具有特定值.为了在C++中进行虚拟化,编译器必须证明该对象是特定类的实例.在第一种情况下,证据似乎更难.但是,虚拟表通常仅在极少数地方进行修改,最重要的是:仅仅因为它看起来更难,并不意味着编译器不是那么好(否则你可能会认为xoring通常比添加两个更快)整数).
不同之处在于,在C++中,编译器可以保证虚拟表地址永远不会改变.在C中,它只是另一个指针,你可能会对它造成任何破坏.
但是,虚拟表通常仅在极少数地方进行修改
编译器在C 中不知道.在C++中,它可以假设它永远不会改变.
是的,如果编译器可以推断出虚拟化类型的确切类型,它就可以“去虚拟化”(甚至内联!)调用。只有当编译器能够保证无论如何这都是所需的函数时,编译器才能做到这一点。
主要关注的基本上是线程。在 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.
}
Run Code Online (Sandbox Code Playgroud)