gpt4 book ai didi

c++ - 避免虚函数

转载 作者:可可西里 更新时间:2023-11-01 16:28:55 28 4
gpt4 key购买 nike

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

void doYourJob();

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

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

 virtual void doYourJob();

但我犹豫要不要这样做。这是一个耗时的程序,虚函数可以大大简化它。此外,此功能是类之间唯一的共同点,并且每个类的 doYourJob 实现方式完全不同。

有没有办法避免将抽象类与虚函数一起使用,或者我将不得不接受它?

最佳答案

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


实现

#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 系统上:

~/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 倍。当然,这特定于所测试的确切系统/编译器标志和版本等,但通常是指示性的。


关于虚拟调度的评论

必须要说的是,虚函数调用的开销很少很重要,而且仅适用于经常调用的琐碎函数(如 getter 和 setter)。即使那样,您也可以提供一个函数来同时获取和设置很多东西,从而最大限度地降低成本。人们太担心虚拟调度了——所以在找到尴尬的替代方案之前一定要进行分析。它们的主要问题是它们执行外联函数调用,尽管它们也使执行的代码离域化,这改变了缓存利用模式(更好或(更经常)更坏)。

关于c++ - 避免虚函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4801833/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com