概述
C++回调函数(callback)与仿函数(functor)的异同 回调函数(callback)与仿函数(functor)很多时候从用途上来看很相似,以致于我们经常将它们相提并论 。例如: inline bool compare(int a, int b) 拟函数调用效果的技术。 如果这里vec, vec2这两个vector的内容一样,那么从执行结果看,使用回调函数compare与使用仿函数 comparer是一样的。 那么,我们应该用回调,还是用仿函数? 很多人都说用仿函数吧,回调函数是丑陋的,代码不太象C++风格。 但其实问题的本质不是在代码风格上,仿函数与回调函数各有利弊,不能一概而论。 仿函数(functor)的优点 仿函数可以不带痕迹地传递上下文参数。而回调技术通常使用一个额外的void*参数传递。这也是多数人 认为回调技术丑陋的原因。 么会性能比仿函数差?我们这里来分析下。我们假设某个函数func(例如上面的std::sort)调用中传递 了一个回调函数(如上面的compare),那么可以分为两种情况: func是内联函数,并且比较简单,func调用最终被展开了,那么其中对回调函数的调用也成为一普通函数 调用(而不是通过函数指针的间接调用),并且如果这个回调函数如果简单,那么也可能同时被展开。在 这种情形下,回调函数与仿函数性能相同。 为存在递归而无法展开)。此时回调函数作为一个函数指针传入,其代码亦无法展开。而仿函数则不同。 虽然func本身复杂不能展开,但是func函数中对仿函数的调用是编译器编译期间就可以确定并进行inline 展开的。因此在这种情形下,仿函数比之于回调函数,有着更好的性能。并且,这种性能优势有时是一种 无可比拟的优势(对于std::sort就是如此,因为元素比较的次数非常巨大,是否可以进行内联展开导致 了一种雪崩效应)。 了回调函数,而不是仿函数,这是因为AutoFreeAlloc要容纳异质的析构函数,而不是只支持某一种类的 析构。这和模板(template)不能处理在同一个容器中支持异质类型,是一个道理。 (一)inline函数(摘自C++ Primer的第三版) 在函数声明或定义中函数返回类型前加上关键字inline即把min()指定为内联。 inline int min(int first, int secend) {/****/}; inline函数对编译器而言必须是可见的,以便它能够在调用点内展开该函数。与非inline 序来说,程序员不能定义这样的min()函数,它在compute.C中指一件事情, 为保证不会发生这样的事情,建议把inline函数的定义放到头文件中。在每个调用该inline函数的 (二)内联函数的编程风格(摘自高质量C++/C 编程指南) 关键字inline 必须与函数定义体放在一起才能使函数成为内联,仅将inline 放在函数声明前面不起任何 作用。 如下风格的函数Foo 不能成为内联函数:
[1] 内联函数是什么? 提高执行效率,尤其是它能够通过调用(“过程化集成”)被编译器优化。 [2] 内联函数是如何在安全和速度上取得折衷? void* 指针对于结构的用户来说是未知的。因此结构的用户不知道如何解释void*指针所指内容,但是存 取函数可以将 void* 转换成适当的隐含类型。这样给出了封装的一种形式。 不幸的是这样做丧失了类型安全,并且也将繁琐的对结构中的每个域的访问强加于函数调用。(如果你允 许直接存取结构的域,那么对任何能直接存取的人来说,了解如何解释 void* 指针所指内容就是必要的 了;这样将使改变底层数据结构变的困难)。 虽然函数调用开销是很小的,但它会被累积。C++类允许函数调用以内联展开。这样让你在得到封装的安 全性时,同时得到直接存取的速度。此外,内联函数的参数类型由编译器检查,这是对 C 的 #define 宏 的一个改进。
因为#define宏是在四处是有害的 和 #define 宏不同的是,内联函数总是对参数只精确地进行一次求值,从而避免了声名狼藉的宏错误。 换句话说,调用内联函数和调用正规函数是等价的,差别仅仅是更快:
宏是有害的;非万不得已不要用。
用。尤其是,如果你将内联函数的定义放在 .cpp 文件中并且在其他 .cpp文件中调用它,连接器将给出 “unresolved external” 错误。
[5] 如何告诉编译器使一个成员函数成为内联函数?
件中并且在其他 .cpp 文件中调用它,连接器将给出“unresolved external”错误。
[6] 有其它方法告诉编译器使成员函数成为内联吗?
阅读的人带来了困难。我们通常更愿意在类体外使用 inline 关键字定义成员函数来避免这种混合。这种 感觉所基于的认识是:在一个面向重用的世界中,使用你的类的人有很多,而建造它的人只有一个(你自 己);因此你做任何事都应该照顾多数而不是少数。
不。 小心过度使用内联函数可能导致代码膨胀。在页面调度环境中,它可能会给执行性能带来负面影响。 代码膨胀术语只表示代码的尺寸会增大(膨胀)。在有关内联函数的上下文中,更关心的是内联函数会增 加执行代码的尺寸,并导致操作系统不稳定。这意味着操作系统要花费大部分的时间从磁盘取出代码。 当然,内联函数也可能减小执行代码的尺寸。看上去反了,其实是真的。特别是,调用函数的代码总量有 时会大于展开的内联函数的代码总量。这样的情况会发生于非常小的函数,当优化器能删除很多冗余代码 时——也就是当优化器能使长的函数变短时,也可能会发生于长的函数。 因此结论就是:没有简单的定论。你必须因地制宜。不要使得答案象这样的单纯化,“不要用内联函数” 或“总是使用内联函数”或“当且仅当函数代码少于 N 行时用内联函数”。这种一刀切的方法可能用起 来非常简单,但是它们产生的并不是最佳结果。 |
最后
以上就是震动毛巾为你收集整理的【zz】C++回调函数(callback)与仿函数(functor)的异同的全部内容,希望文章能够帮你解决【zz】C++回调函数(callback)与仿函数(functor)的异同所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复