百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

解决虚函数性能问题,选择模板还是继承?

qihemm 2025-06-10 16:25 29 浏览 0 评论

根据上篇文章《C++虚函数会导致性能开销大?》,我们了解了虚函数的机制,以及虚函数对性能的影响。

只能在运行期间解析的虚函数调用是不允许使用内联的。这往往会造成性能问题,该问题我们必须解决。因为函数调用的动态绑定是继承的结果,所以消除动态绑定的一种方法是用基于模板的设计来替代继承模板把解析的步骤从运行期间提前到编译期间,从这个意义上说,模板提高了性能。而对于我们所关心的编译时间,适当增加也是可以接受的。

下面我们从代码的角度进行说明。

代码示例

假设我们要开发一个线程安全的string类,它可以被并发线程安全地使用。

第一步,先设计Locker抽象基类将声明公共接口:

//接口类
class Locker
{
    Locker(){};
    virtual ~Locker(){}; 
    virtual void lock()=0; 
    virtual void unlock()=0; 
};

CriticalSectionLock和MutexLock将从基类Locker派生出来。

//派生类
class CriticalSectionLock:public Lock
{
....
};
class MutexLock:public Lock
{
....
};

第二步,从现有的标准string类派生出线程安全的string类,有如下三种方式:

● 继承 (存在性能问题)

可以派生出一个单独的ThreadSafeString类,它包含指向Locker对象的指针(建立成员变量:锁基类对象指针,在初始化列表进行初始化,传入不同类型,多态机制)。在运行期间通过多态机制选择特定的同步机制。

把所选择的同步机制(具体类型)作为构造函数的参数:

//通过多态机制实现不同类型锁的使用
class ThreadSafeString : public string 
{
public:
    ThreadSafeString (const char *s, Locker *lockPtr): string(s), pLock(lockPtr) {};
    ...
    int length();

private:
    Locker *pLock;//基类类型,锁类型当前不固定,使用多态机制在运行时才能知道具体的子类类型
};

int ThreadSafeString::length()
{
    pLock->lock();
    int len = string::length();
    pLock->unlock();

    return len;
}

//使用锁
CriticalSectionLock cs;
ThreadSafeString csString("Hello", &cs);//在运行时传入具体的子类类型

MutexLock mtx;
ThreadSafeString csString("Hello", &mtx);//在运行时传入具体的子类类型

这种实现比较简洁。但是它确实带来了性能的损失:

虚函数调用lock()和unlock()仅在执行期间解析,因此不能对它们内联


硬编码

可以从string类中派生出三个独立类:CriticalSection String、MutexString和SemaphoreString。每个类实现各自名字所代表的同步机制(建立成员变量:锁子类对象)。

例如CriticalSectionString类:

//用标准的string类被用做基类。每个由它派生的类实现某一特定的同步机制。
class CriticalSectionString : public string
{
public:
    ...
    int length();
private:
    CriticalSectionLock cs;//锁类型固定,不是基类类型,而是具体的子类类型,不使用多态机制
};

int CriticalSectionString::length()
{
    cs.lock();
    int len = string::length();
    cs.unlock();

    return len;
}

这种设计在性能上具有优势

这个线程安全的string类在编译期间指定了特定的同步类(指定了固定的子类类型,例如CriticalSectionString)。因此,编译器可以绕过动态绑定来选择正确的lock()和unlock()方法。更重要的是,这种设计允许编译器内联这两个方法

该设计的不足之处:

在于需要为每种同步机制编写各自的string类,导致代码重用性较低


● 模板

基于模板的string类,该类由Locker类型参数化后得到(模板类中建立成员变量:默认类型是基类锁对象)

基于模板的设计结合了两方面的优点: 重用和效率。

//ThreadSafeString是作为模板实现的,它的参数由LOCKER模板参数决定
template <class LOCKER>
class ThreadSafeString : public string 
{
public:
    ThreadSafeString(const char *s) : string(s) {}
    ...
    int length();

private:
    LOCKER lock;//锁类型固定,由LOCKER模板参数决定(实际执行处传入的是子类类型)
};

template <class LOCKER>
inline 
int ThreadSafeString<LOCKER>::length()
{
    lock.lock();
    int len = string::length();
    lock.unlock();

    return len;
}

//实例化模板
ThreadSafeString <CriticalSectionLock> csString ="hello";
或者使用互斥
ThreadSafeString <MutexLock> mtxString = "hello";

这种设计也避免了对lock和unlock的虚函数调用。ThreadSafeString声明在实例化模板时选择特定的同步类型。如同硬编码一样,它使编译器可以解析这两个虚函数调用并且内联它们。

模板把计算从执行期间提前到编译期间来做,并且在编译时使用内联,因此提高了性能。


从上面几种方式能得出下面的结论:

①在测试类里建立lock类型的成员对象,如果类型已经固定成子类类型,则lock类的虚函数可以被适当的编译器静态地解析;

②在测试类里,类型在编译时具体类型还未固定(暂时是基类类型,使用多态机制运行时才能固定类型),则lock类的虚函数不能被编译器静态地解析;


总结

虚函数的代价在于无法内联函数调用,因为这些调用是在运行时动态绑定的。唯一潜在的效率问题是从内联获得的速度(如果可以内联的话)。但对于那些代价并非取决于调用和返回开销的函数来说,内联效率不是问题。

模板比继承提供更好的性能。它把对类型的解析提前到编译期间,我们认为这是没有成本的

相关推荐

VLOOKUP的18种高阶用法大公开!99%的人都不知道的神操作!

作为被头条用户催更的Excel课代表,今天带来让HR追着要模板、让老板主动加薪的VLOOKUP终极指南!从基础到高阶一网打尽,文末送36个行业专用模板!一、为什么你的VLOOKUP总报错?血泪大数据...

Vlooup公式,2种模糊查找匹配,1分钟学会

工作中,VLOOKUP公式使用频率是很高的,用来各种查找匹配问题今天我们分享两种模糊查找匹配问题,一种是文本的模糊查找匹配,一种是数字的模糊查找匹配问题1、文本模糊查找匹配使用模拟数据举个例子,原始数...

与vlookup功能相似的函数,照样搞定表格数据查询,简单还实用

在日常表格数据处理工作,说到数据查询,很多小伙伴首先想到的是Vlookup函数,老师的教程中也多次讲到Vlookup函数的用法和实例。其实在Excel中还有其他的数据查询函数公式或技巧,今天我们先来学...

别再折腾VLOOKUP了!DGET逆向查找10秒通关,小白必看

今天要掀翻一个“过气网红”——VLOOKUP!你是不是也经历过这些崩溃瞬间:逆向查找要交换列顺序,复制粘贴到手软!多条件查找要嵌套MATCH,公式长到怀疑人生!别忍了!今天教你用DGET函数一键封...

职场新人必学!VLOOKUP函数10分钟速成指南

正文:"今天来讲解办公人入职期初函数VLOOKUP,这是所有职场人最重要也是最基础的技能。掌握它,90%的数据查找再不用求人!特别献给刚入职场的你——别让Excel成为加班理由。"——...

巧用Vlookup函数揪出“第三者”(vlookup第三个参数是什么)

在一张Excel表格的重复记录中,让你快速列出每种不同物品第2次或第n次出现的记录,你会怎么做?Vlookup函数就有这个本事。举例来说,产品或者物流表格中往往会记录有同一货物的多笔数据(如下图的今日...

分享12个VLOOKUP超经典用法(vlookup通俗易懂)

刚毕业那会,面试的时候经常会被问到会不会用Excel?我就理直气壮地回答:“会啊。”毕竟,简历上可是写着熟练。接着面试官扔出一句“那你会VLOOKUP吗?”我还是会一口咬定:“我会。“其实,我都没用过...

查找匹配别只知道Vlookup,Sumifs也可以!

工作中遇到查找匹配问题的时候,大家第一反应是不是都想到的Vlookup公式呢,有没有小伙伴们给Sumifs一点点机会的呢,有时候Sumifs比Vlookup更好用1、Vlookup公式举个例子,左边是...

Excel函数讲解:VLOOKUP函数,轻松玩转数据查找

常用函数系列教学:VLOOKUP函数讲解(46)。不懂VLOOKUP函数怎么高效查找数据?闲话少叙直接开讲。基本含义:VLOOKUP函数用于在表格按垂直方向(到)上查找返回行数据。如何使用及注意事项?...

CHOOSEROWS+CHOOSECOLS原来是一个超级查找函数组合!

场景一:要在学生名册中,抽查一名学生成绩。公式:=CHOOSEROWS(A1:D5,2)解析:第一参数A1:D5为数据区域,第二参数2表示提取第2行数据。把数据区域改为A2:D5,结合RANDBETW...

数据查询不止有vlookup函数,自定义zlookup函数查询操作更高效

Excel数据查询,相信大家首先会想到vlookup函数。毋庸置疑vlookup函数在Excel数据查询中作用是非常的强大。但是它也有一些不能实现的数据查询。如上图所示,我们需要根据人员的出现次数,提...

「EXCEL进阶」VLOOKUP函数怎么查询一个值返回多个结果

前言:VLOOKUP函数一般一次只能返回一个结果,本例介绍通过辅助列的方法使VLOOKUP函数查询一个值,返回这个值对应的多个结果。使用场景举例:根据表格中同一数值,返回对应值的多个结果。比如这张数据...

WPS查找能手VLOOKUP函数使用方法讲解

各位同学好!今天我们来深度剖析WPS最实用的查找工具——VLOOKUP函数。这个函数能帮你在表格中快速定位并提取所需数据,可以帮你快速核对两批数据差异,还可以合并多个表格的关联信息,甚至可以帮你制作动...

Excel常用10个函数:跨表查找Vlookup,适用于大数据中查找精确值

Hello大家好,我是Office米,今天,我们将和大家一起分享交流,常用的10个函数之一:查找引用函数VLOOKUP。在说VLOOKUP函数之前,我们要先了解,平时Excel日常工作中会遇到哪些问题...

掌握了这个套路,无论用 Excel vlookup 函数查找第几次结果都很轻松

用vlookup查找默认情况下是一对一出结果,如果要一对多查找,就需要用到各种技巧,具体方法我写过非常多了,可以搜索一下历史记录。只要掌握了今天这个套路,无论你想查找第几次重复值,都易如反掌。案例...

取消回复欢迎 发表评论: