C++链接器对仅从程序中的一个类使用的虚拟方法是否明智

Is the C++ linker smart about virtual methods used only from one class in a program?

本文关键字:一个 虚拟 是否 方法 链接 程序 C++      更新时间:2023-10-16

我在一个单元测试文化性极低的项目中工作。我们几乎没有单元测试,而且每个API都是静态的。

为了能够对我的一些代码进行单元测试,我创建了像这样的包装器

class ApiWrapper {
    virtual int Call(foo, bar) {
        return ApiCall(foo, bar);
    }
};

现在在我的函数中:

int myfunc() {
    APiCall(foo, bar);        
}

我知道:

int myfunc(ApiWrapper* wrapper) {
    wrapper->Call(foo, bar);        
}

通过这种方式,我可以嘲笑这样的功能。问题是,一些同事抱怨生产代码不应该受到可测试性需求的影响——我知道这是无稽之谈,但却是现实。

无论如何,我相信我在某个地方读到过,编译器实际上很聪明,可以用直接调用替换未使用的多态行为。。。或者,如果没有覆盖虚拟方法的类,它将变为"正常"。

我进行了实验,在gcc 4.8上,它不内联或直接调用虚拟方法,而是创建vt.

我试着在谷歌上搜索,但没有找到任何关于这方面的信息。这是一件事还是我记错了。。。或者我必须做些什么来向链接器解释这一点,优化标志或其他什么?

请注意,在生产环境中,此类是最终类,而在测试环境中则不是。这正是链接器必须聪明并检测到的。

C++编译器只有在确定实际类型的情况下才会用直接调用替换多态调用。

因此,在下面的片段中,它将被优化:

void f() {
  ApiWrapper x;
  x.Call();  // Can be replaced
}

但在一般情况下,它不能:

void f(ApiWrapper* wrapper) {
  wrapper->Call();  // Cannot be replaced
}

您还为您的问题添加了两个条件:

如果没有重写虚拟方法的类,那么它将变为"正常"。

这无济于事。C++编译器和链接器都不会查看类的整体来搜索是否存在继承器。无论如何,这都是徒劳的,因为您总是可以动态加载一个新类的实例。

顺便说一句,这种优化确实是由一些JVM执行的(称为去机会化),因为在Java领域有一个类加载器,它可以知道当前加载了哪些类。

在生产中,这一类是最后的

那会有帮助的!例如,如果方法/方法的类标记为final,Clang将把虚拟调用转换为非虚拟调用。