C++链接器对仅从程序中的一个类使用的虚拟方法是否明智
Is the C++ linker smart about virtual methods used only from one class in a program?
我在一个单元测试文化性极低的项目中工作。我们几乎没有单元测试,而且每个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将把虚拟调用转换为非虚拟调用。
相关文章:
- 为什么在std::optional的某些实现中有一个虚拟工会成员?
- 有什么理由在没有继承的情况下声明一个虚拟方法
- 在这种情况下,我的派生类是否还需要一个虚拟析构函数
- void f()=0是一个虚拟方法
- 为什么不使用只有一个虚拟继承的钻石继承呢
- 错过了一个虚拟函数调用
- 从static中派生的基类实现一个虚拟方法
- 如果你的基类有一个虚拟析构函数,你自己的析构函数就会自动变成虚拟的
- 用另一个虚拟析构函数覆盖"empty"虚拟析构函数有什么害处吗?
- 是将基类强制转换为派生类更好,还是在基类上创建一个虚拟函数更好
- 如何实现一个虚拟函数equal()
- 如果类至少包含一个虚拟函数,是否总是需要将析构函数声明为虚拟函数
- 我的基类中有一个虚拟析构函数和一个数组.我怎样才能让它发挥作用
- 我可以调用一个虚拟函数来初始化基类子对象吗
- 是否可以确定/断言,如果一个虚拟函数被覆盖,另一个也被覆盖
- 这是另一个虚拟模板解决方案
- 我们可以有一个虚拟静态方法吗?(c++)
- 创建一个虚拟的 HWND 以传递给 Direct3D
- 将两个目录合并到一个虚拟目录中用于编译/别名包含目录
- 为什么我应该为C++中的抽象类声明一个虚拟析构函数