C++仿函数的优点

转自:http://kevincg.wordpress.com/2009/06/04/c-%E4%BB%BF%E5%87%BD%E6%95%B0functor/

仿函數跟callback函數很類似,但是有本質上的不太一樣

仿函數似乎就是把Struct或Class假裝成一個函式的樣子。

舉個例,有這樣的一個struct
struct test
{
       int a;
       int b;
}

有個sort排列要求按a的大小來排,就是
struct sortfunction
{
bool operator()( const test &ta, const test &tb )
{return ta.a < tb.a; }
}

接著就能這樣使用了
std::vector<test > v;
std:: sort( v.begin(), v.end(), sortfunction() )

為什麼不把sortfunction直接寫成一個Function就好,也是可以,但是以類或者結構來處理可以得到更多彈性。

仿函數(functor)的優點

如果可以用仿函數實現,那麼你應該用仿函數,而不要用CallBack。原因在於:

  • 仿函數可以不帶痕跡地傳遞上下文參數。而CallBack技術通常使用一個額外的void*參數傳遞。這也是多數人認為CallBack技術醜陋的原因。
  • 更好的性能。

仿函數技術可以獲得更好的性能,這點直觀來講比較難以理解。你可能說,CallBack函數都寫成inline了,怎麼會性能比仿函數差?我們這裡來分析下。我們假設某個函數func(例如上面的std::sort)調用中傳遞了一個CallBack函數,那麼可以分為兩種情況:

  • func是inline函數,並且比較簡單,func呼叫最後被展開了,那麼其中對CallBack函數的呼叫也成為一普通函數呼叫(而不是通過函數指標的間接呼叫),並且如果這個CallBack函數很簡單,那麼也可能同時被展開。在這種情形下,CallBack函數與仿函數性能相同。
  • func是非inline函數的話,或者比較複雜而無法展開(例如上面的std::sort,我們知道它是快速排序,函數因為存在遞回而無法展開)。此時CallBack函數作為一個函數指標傳入,他的程式碼也是無法被展開。而仿函數則不同。雖然func本身複雜不能展開,但是func函數中對仿函數的呼叫是編譯器編譯期間就可以確定並進行inline展開的(yasi:为什么仿函数在编译期间就可以展开?)。因此在這種情形下,仿函數比之於CallBack函數,有著更好的性能。並且,這種性能優勢有時是一種無可比擬的優勢(對於std::sort就是如此,因為元素比較的次數非常巨大,是否可以進行inline展開導致了一種雪崩效應)

仿函數(functor)不能做的?

話又說回來了,仿函數並不能完全取代CallBack函數所有的應用場合。例如,我在std::AutoFreeAlloc中使用了CallBack函數,而不是仿函數,這是因為AutoFreeAlloc要容納異質的解構函數,而不是只支持某一種類的解構。這和模板(template)不能在同一個容器中支持異質類型,是一樣的。


### C++仿函数的概念及用法 #### 什么是仿函数仿函数(Functor),也被称为函数对象,在 C++ 编程中是指能够像函数一样调用的对象。这种能力来源于重载 `operator()` 运算符[^1]。通过这种方式,仿函数不仅具备函数的行为特征,还拥有类的属性存储功能。 #### 为什么使用仿函数? 相比于普通的函数指针或 lambda 表达式,仿函数具有更大的灵活性和可扩展性。它允许开发者将状态信息嵌入其中,从而实现更复杂的功能逻辑[^3]。此外,仿函数可以通过 STL 提供的标准算法无缝集成,简化代码结构并提高效率[^2]。 --- #### 自定义仿函数示例 下面展示如何创建一个简单的仿函数来计算两个整数之差: ```cpp #include <iostream> using namespace std; // 定义一个仿函数类 class Subtract { public: int operator()(int a, int b) const { return a - b; // 实现减法运算 } }; int main() { Subtract sub; cout << "Result of subtraction: " << sub(10, 5) << endl; // 输出结果为 5 return 0; } ``` 上述例子展示了如何通过重载 `operator()` 来使类实例表现得如同函数一般[^4]。 --- #### 利用 STL 的内置仿函数 C++ 标准库提供了多种预定义好的仿函数,比如用于比较大小的 `std::less` 和 `std::greater` 或者执行加法/乘法操作的 `std::plus` 和 `std::multiplies` 等。以下是利用这些工具的一个简单案例——对数组元素求平方根后再相加: ```cpp #include <vector> #include <algorithm> #include <functional> // 引入 functional 头文件以访问内置仿函数 #include <cmath> double sumOfSquares(const vector<int>& nums) { double total = 0.0; for (const auto& num : nums) { total += sqrt(abs(num)); // 对每个数值取绝对值后开方再累加 } return total; } int main(){ vector<int> data {-9,-4,0,4,9}; // 使用 std::bind 将 sqrt 函数绑定至 abs 结果上形成新的仿函数形式处理流程 transform(data.begin(),data.end(), back_inserter(vector<double>()), bind(sqrt<>, placeholders::_1)); cout<<"Sum:"<<sumOfSquares(data)<<endl; return EXIT_SUCCESS; } ``` 注意此段代码存在语法错误仅作示意用途,请参照实际需求调整具体实现细节。 --- #### 总结 仿函数作为一种强大的机制,既保留了传统函数的优点又增加了额外的状态管理能力,非常适合于需要频繁重复某些特定模式的任务场景下应用。同时借助 STL 库的支持可以让我们的程序更加简洁明快而不失功能性。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值