首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >避免虚函数

避免虚函数
EN

Stack Overflow用户
提问于 2011-01-26 14:14:30
回答 4查看 6.6K关注 0票数 9

所以假设我想创建一系列的类,每个类都有一个成员函数,具有相同的东西。让我们调用该函数

代码语言:javascript
复制
void doYourJob();

我希望最终将所有这些类都放到同一个容器中,这样我就可以遍历它们并让每个类执行“doYourJob()”。

显而易见的解决方案是使用函数创建一个抽象类

代码语言:javascript
复制
 virtual void doYourJob();

但我对此犹豫不决。这是一个耗时的程序,一个虚拟函数会使它变得非常糟糕。此外,此函数是类之间唯一的共同点,并且每个类的doYourJob实现方式完全不同。

有没有一种方法可以避免使用具有虚函数的抽象类,或者我必须接受它?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2011-01-26 14:20:19

虚函数的成本并不高。它们是间接调用,基本上类似于函数指针。What is the performance cost of having a virtual method in a C++ class?

如果您处于每次调用周期都很重要的情况下,也就是说,您在函数调用中所做的工作很少,并且在性能关键型应用程序中从内部循环调用它,那么您可能需要一种完全不同的方法。

票数 6
EN

Stack Overflow用户

发布于 2011-01-26 14:21:57

如果您需要这样的速度,可以考虑在对象中嵌入一个“类型(-identifying)号”,并使用语句选择特定于类型的代码。这可以完全避免函数调用开销--只需做一个局部跳转。你不会比这更快的。成本(在可维护性、重新编译依赖等方面)是强制特定类型功能的本地化(在切换中)。

IMPLEMENTATION

代码语言:javascript
复制
#include <iostream>
#include <vector>

// virtual dispatch model...

struct Base
{
    virtual int f() const { return 1; }
};

struct Derived : Base
{
    virtual int f() const { return 2; }
};

// alternative: member variable encodes runtime type...

struct Type
{
    Type(int type) : type_(type) { }
    int type_;
};

struct A : Type
{
    A() : Type(1) { }
    int f() const { return 1; }
};

struct B : Type
{
    B() : Type(2) { }
    int f() const { return 2; }
};

struct Timer
{
    Timer() { clock_gettime(CLOCK_MONOTONIC, &from); }
    struct timespec from;
    double elapsed() const
    {
        struct timespec to;
        clock_gettime(CLOCK_MONOTONIC, &to);
        return to.tv_sec - from.tv_sec + 1E-9 * (to.tv_nsec - from.tv_nsec);
    }
};

int main(int argc)
{
  for (int j = 0; j < 3; ++j)
  {
    typedef std::vector<Base*> V;
    V v;

    for (int i = 0; i < 1000; ++i)
        v.push_back(i % 2 ? new Base : (Base*)new Derived);

    int total = 0;

    Timer tv;

    for (int i = 0; i < 100000; ++i)
        for (V::const_iterator i = v.begin(); i != v.end(); ++i)
            total += (*i)->f();

    double tve = tv.elapsed();

    std::cout << "virtual dispatch: " << total << ' ' << tve << '\n';

    // ----------------------------

    typedef std::vector<Type*> W;
    W w;

    for (int i = 0; i < 1000; ++i)
        w.push_back(i % 2 ? (Type*)new A : (Type*)new B);

    total = 0;

    Timer tw;

    for (int i = 0; i < 100000; ++i)
        for (W::const_iterator i = w.begin(); i != w.end(); ++i)
        {
            if ((*i)->type_ == 1)
                total += ((A*)(*i))->f();
            else
                total += ((B*)(*i))->f();
        }

    double twe = tw.elapsed();

    std::cout << "switched: " << total << ' ' << twe << '\n';

    // ----------------------------

    total = 0;

    Timer tw2;

    for (int i = 0; i < 100000; ++i)
        for (W::const_iterator i = w.begin(); i != w.end(); ++i)
            total += (*i)->type_;

    double tw2e = tw2.elapsed();

    std::cout << "overheads: " << total << ' ' << tw2e << '\n';
  }
}

性能结果

在我的Linux系统上:

代码语言:javascript
复制
~/dev  g++ -O2 -o vdt vdt.cc -lrt
~/dev  ./vdt                     
virtual dispatch: 150000000 1.28025
switched: 150000000 0.344314
overhead: 150000000 0.229018
virtual dispatch: 150000000 1.285
switched: 150000000 0.345367
overhead: 150000000 0.231051
virtual dispatch: 150000000 1.28969
switched: 150000000 0.345876
overhead: 150000000 0.230726

这表明内联类型-数字切换方法的速度大约是(1.28 - 0.23) / (0.344 - 0.23) = 9.2倍。当然,这是特定于确切的系统测试/编译器标志和版本等,但通常是指示性的。

评论RE虚拟派单

但必须指出的是,虚函数调用的开销很少有意义,而且只适用于经常调用的琐碎函数(如getter和setter)。即使这样,您也可以提供单个函数来一次性获取和设置大量内容,从而将成本降至最低。人们对虚拟分派方式的担忧太多了--所以在找到尴尬的替代方案之前,也要做分析。它们的主要问题是它们执行离线函数调用,尽管它们也会使执行的代码去本地化,这会改变缓存利用模式(更好或(更经常)更糟)。

票数 9
EN

Stack Overflow用户

发布于 2011-01-26 14:17:58

我担心循环中的一系列dynamic_cast检查会使性能变得比虚函数更差。如果你打算把它们都放在一个容器里,它们需要有一些共同的类型,所以你也可以使它成为一个纯虚拟的基类,里面有那个方法。

在那个上下文中,虚函数分派没有那么多功能: vtable查找、对所提供的this指针的调整以及间接调用。

如果性能如此重要,您可以为每个子类型使用单独的容器,并单独处理每个容器。如果顺序很重要,你会做很多后空翻,这样虚拟分派可能会更快。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4801833

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档