class A {
public:
virtual void f() = 0;
};
class B : public A {
public:
void f() final override { };
};
int main() {
B* b = new B();
b->f();
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,编译器是否仍需要执行v表查找b->f();,或者是否可以B::f()直接调用,因为它已被标记final?
我想知道std::variant性能。什么时候不应该使用它?似乎虚拟函数仍然比使用虚拟函数好得多,std::visit这让我感到惊讶!
在pattern checking解释std::holds_alternatives和overloaded方法之后,Bjarne Stroustrup在“ C ++之旅”中谈到了这一点:
这基本上等效于虚拟函数调用,但可能更快。与所有性能要求一样,当性能至关重要时,应通过测量来验证“可能更快”。对于大多数用途,性能差异不明显。
我已经确定了一些想到的方法,这些是结果:
http://quick-bench.com/N35RRw_IFO74ZihFbtMu4BIKCJg
如果启用优化,您将得到不同的结果:
http://quick-bench.com/p6KIUtRxZdHJeiFiGI8gjbOumoc
这是我用于基准测试的代码;我确信,有更好的方法来实现和使用变体来代替虚拟关键字来使用它们(继承与std :: variant):
删除旧代码;看看更新
任何人都可以解释一下实现此用例的最佳方法是什么,这std::variant使我不得不进行测试和基准测试:
我当前正在实现RFC 3986,它是“ URI”,在我的用例中,该类将更多地用作const,并且可能不会做很多更改,并且用户更有可能使用此类来查找每个特定类URI的一部分,而不是生成URI;因此,有意义的是使用std::string_viewURI的每个段,而不是将其分开std::string。问题是我需要为其实现两个类。当我只需要一个const版本时;另一个用于当用户想要创建URI而不是提供一个URI并进行搜索时。
所以我用a template来解决有其自身问题的内容;但是后来我意识到我可以使用std::variant<std::string, std::string_view>(或者也许std::variant<CustomStructHoldingAllThePieces, std::string_view>);所以我开始研究以查看是否确实有助于使用变体。从这些结果看,似乎要使用继承,virtual如果我不想实现两个不同的const_uriand uri类,那是我最好的选择。
您认为我该怎么办?
感谢@gan_在我的基准代码中提到并解决了起吊问题。
http://quick-bench.com/Mcclomh03nu8nDCgT3T302xKnXY
我对try-catch hell的结果感到惊讶,但是由于有了现在有意义的注释。
我删除了该try-catch方法,因为它确实很糟糕。而且还随机更改了所选值,并且通过它的外观,我看到了更现实的基准。virtual毕竟这似乎不是正确的答案。
http://quick-bench.com/o92Yrt0tmqTdcvufmIpu_fIfHt0
http://quick-bench.com/FFbe3bsIpdFsmgKfm94xGNFKVKs(无内存泄漏大声笑)
我消除了生成随机数的开销(我在上一次更新中已经做到了,但是似乎我抓取了错误的URL作为基准),并添加了EmptyRandom来理解生成随机数的基准。并且对Virtual进行了一些小的更改,但我认为它没有任何影响。
http://quick-bench.com/EmhM-S-xoA0LABYK6yrMyBb8UeI
http://quick-bench.com/5hBZprSRIRGuDaBZ_wj0cOwnNhw(已删除了“虚拟”,因此您可以更好地比较其余虚拟)
正如豪尔赫·贝隆(Jorge Bellon)在评论中说的 …
比较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++ ×3
c ×1
c++11 ×1
c++17 ×1
final ×1
inheritance ×1
performance ×1
std-variant ×1
virtual ×1