仿函數

来源:https://www.cnblogs.com/Vergissmeinnicht-rj/archive/2023/03/08/17196619.html
-Advertisement-
Play Games

C++中的仿函數(function object)是一個重載了函數調用運算符(operator())的類或結構體,在使用時可以像函數一樣調用。通過仿函數,C++程式員可以更加靈活地實現自己的演算法。 ...


仿函數

1.什麼是仿函數

1.定義和作用

仿函數是一種重載了函數調用運算符(operator())的類或結構體,它可以像函數一樣被調用。仿函數可以在很多STL演算法中使用,例如sort、for_each、transform等,可以自定義排序規則、操作、條件等等。通過仿函數,C++程式員可以更加靈活地實現自己的演算法。

與普通函數不同,仿函數可以保存狀態,因此在使用仿函數時可以靈活地傳遞參數併進行計算,非常適用於一些複雜的演算法和數據結構的實現。

2.仿函數與函數指針的區別

在C++中,函數指針可以作為參數傳遞和返回值,但是函數指針只能指向函數,無法指向類成員函數和lambda表達式。而仿函數可以作為一種通用的函數封裝,可以指向函數、類成員函數以及lambda表達式,並且可以保存狀態。因此,仿函數比函數指針更加靈活和可擴展。

3.仿函數的調用方式

仿函數可以像函數一樣被調用

class MyFunctor {
public:
    void operator() (int i) {
        cout << i << endl;
    }
};
MyFunctor myFunctor;
myFunctor(123);
//結果
//123

在STL演算法中,仿函數也可以作為函數參數傳遞

vector<int> vec{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
sort(vec.begin(), vec.end(), greater<int>());
for_each(vec.begin(), vec.end(), MyFunctor());

其中,greater()是一個內置的仿函數對象,用於實現降序排列。

2.仿函數的分類

1.一元仿函數和二元仿函數

一元仿函數是指只有一個參數的仿函數

template <typename T>
struct MyFunctor1 {
    void operator() (T val) {
        cout << val << endl;
    }
};
template <typename T>
struct MyFunctor2 {
    T operator() (T val) {
        return val * val;
    }
};

二元仿函數是指有兩個參數的仿函數

template <typename T>
struct MyFunctor3 {
    bool operator() (T val1, T val2) {
        return val1 < val2;
    }
};

在STL演算法中,一元仿函數和二元仿函數通常用於排序、查找、遍歷等操作。

2.函數對象與謂詞

  • 函數對象:返回值為任意類型的仿函數,例如std::plus,std::minus
  • 謂詞:返回值為bool類型的仿函數,例如std::less,std::greater

3.函數適配器

函數適配器是一種特殊的仿函數,它用於將一個仿函數適配到另一個仿函數或函數對象上。STL中常用的函數適配器有:bind1st、bind2nd、not1、not2、logical_and、logical_or等

template <typename T>
struct MyFunctor4 {
    bool operator() (T val) {
        return val > 0;
    }
};
vector<int> vec{3, -1, 4, -1, 5, -9, 2, -6, 5, 3, -5};
count_if(vec.begin(), vec.end(), not1(MyFunctor4<int>()));
//結果
//3

其中,not1是一個函數適配器,它將MyFunctor4適配成一個返回相反值的仿函數對象,從而統計出vec中小於等於0的元素個數。

3.仿函數的實現

1.重載函數調用運算符

仿函數的實現首先要重載函數調用運算符(operator()),並根據需要定義參數和返回值

template <typename T>
struct MyFunctor1 {
    void operator() (T val) {
        cout << val << endl;
    }
};
template <typename T>
struct MyFunctor2 {
    T operator() (T val) {
        return val * val;
    }
};

其中,MyFunctor1是一個一元仿函數,用於輸出參數值,而MyFunctor2是一個一元仿函數,用於計算參數的平方。

2.使用模板類

仿函數通常是一個模板類,可以支持不同的參數類型

template <typename T>
struct MyFunctor3 {
    bool operator() (T val1, T val2) {
        return val1 < val2;
    }
};

其中,MyFunctor3是一個二元仿函數,用於比較兩個參數的大小。

3.使用函數適配器

仿函數也可以使用函數適配器來實現。例如,使用bind1st函數適配器將一個二元仿函數適配成一個一元仿函數

template <typename T>
struct MyFunctor4 {
    bool operator() (T val1, T val2) {
        return val1 < val2;
    }
};
MyFunctor4<int> myFunctor;
bind1st(myFunctor, 10)(5);
//結果
//false

其中,bind1st將myFunctor適配成一個只有一個參數的仿函數,第一個參數被綁定為10,然後用5調用這個仿函數,返回false。

4.實現一個簡單的加法仿函數

class AddFunctor{
public:
    AddFunctor(int n):m_n(n){}
    int operator()(int x) const{
        return x + m_n;
    }
private:
    int m_n;
}

在上面的代碼中,我們定義了一個AddFunctor類,它帶有一個整型參數n,表示每次調用要加上的值。在調用運算符()時,返回x加上m_n的結果。

使用AddFunctor類,可以有如下示例

int main()
{
	AddFunctor add(3);
    int result = add(5);	//結果為8
    return 0;
}

4.仿函數的應用

在STL中,許多演算法都需要使用仿函數。例如:

  • std::sort():對給定的序列進行排序,需要提供一個比較函數,用於指定排序的規則
  • std::for_each():對給定的序列中的每個元素執行指定的操作,需要提供一個函數對象,用於指定操作
  • std::find_if():在給定的序列中查找符合指定條件的第一個元素,需要提供一個謂詞,用於指定條件

通過使用仿函數,我們可以將演算法和數據結構解耦,是的演算法更加通用和靈活。

5.仿函數的註意事項

在使用仿函數時需要註意以下幾點

  • 仿函數的效率問題:由於仿函數在調用時需要進行對象的構造和析構,因此在一些需要頻繁調用的場景中,使用仿函數可能會影響演算法的效率
  • 仿函數的線程安全性:由於仿函數中可能會保存狀態,因此在多線程環境下使用時需要註意線程安全性問題

6.仿函數的優化

1.使用constexpr

在C++11中,可以使用constexpr關鍵字來聲明一個函數或變數是常量表達式,可以在編譯時計算。如果一個仿函數的operator()是一個常量表達式,可以使用constexpr來進行優化

template <typename T>
struct MyFunctor1 {
    constexpr T operator() (T val) const {
        return val * val;
    }
};

這樣,當使用MyFunctor1時,如果參數是一個常量表達式,那麼編譯器就可以在編譯時計算出結果,從而提高程式的執行效率。

2.使用inline

仿函數可以使用inline關鍵字來聲明為內聯函數,從而在編譯時將函數體直接嵌入到調用處,避免了函數調用的開銷

template <typename T>
struct MyFunctor2 {
    inline T operator() (T val) const {
        return val * val;
    }
};

3.使用lambda表達式

C++11引入了lambda表達式,可以方便地定義一個匿名仿函數。與普通的仿函數相比,使用lambda表達式可以減少代碼的冗餘,從而提高程式的可讀性和維護性

vector<int> vec{3, -1, 4, -1, 5, -9, 2, -6, 5, 3, -5};
auto count = count_if(vec.begin(), vec.end(), [](int val) { return val > 0; });

其中,lambda表達式[] (int val) { return val > 0; }定義了一個匿名仿函數,用於統計vec中大於0的元素個數。

本文來自博客園,作者:Vergissmeinnicht_z,轉載請註明原文鏈接:https://www.cnblogs.com/Vergissmeinnicht-rj/p/17196619.html


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前端路由的設置: Vue2路由(vue-router3) 安裝插件 npm i vue-router@3 router/index.js文件設置 import Vue from "vue"; import VueRouter from "vue-router"; Vue.use(VueRouter) ...
  • JavaScript中的發佈/訂閱模式(Pub/Sub)是一種常用的設計模式。它允許在應用程式中定義對象之間的一對多的依賴關係,當一個對象的狀態發生變化時,所有依賴於它的對象都會被通知和更新。 在發佈/訂閱模式中,有兩種類型的對象:發佈者和訂閱者。發佈者是事件的發出者,它通常維護一個事件列表,並且可 ...
  • 在各種場景的開發中Dialog組件的出現頻率都是非常高的,Dialog組件作為一個容器組件受容器內業務代碼複雜度的影響,代碼行數、變數及函數的定義可能會很多,這樣的組件就一定要考慮封裝使用,以保證主流程代碼的簡潔。下麵一起來看一下如何利用面向對象的思想來封裝它吧~ ...
  • w3c機構:規定網頁分成三個部分:結構、樣式、表現形式。 無序列表: (每個列表左側都有實心黑點,可以用css去掉) <ul> <li>列表1</li> <li>列表2</li> </ul> 快捷方式:ul>li*數量 tab <ul></ul>中只能嵌套<li></li> <li></li>之間相 ...
  • 這篇文章主要描述如何在使用消息隊列時避免丟消息,包括檢測消息丟失的方法以及消息從生產到完成消費的過程中,經歷的生產、存儲和消費這三個階段是分別如何保證消息可靠傳遞的。 ...
  • 一、文件系統存儲 電腦剛開始出現的時候,那時候沒有硬碟,只有記憶體,數據不會進行存儲,一般只用於科技計算,計算完輸出結果後,程式就撤出記憶體了。後來隨著技術發展,有了硬碟、文件,在文件的基礎上有了文件系統。文件系統可以滿足數據存放和查找的需求。 文件系統作為資料庫用了一段時間,當數據越來越多、規模越來 ...
  • 這篇文章描述如何使用消息隊列中的事務消息機制實現分散式事務。事務消息適用於需要非同步更新數據,並且對數據實時性要求不太高的場景。 ...
  • ##一、通訊錄準備 #####1. 通訊錄信息的準備 #####2. 通訊錄功能的框架 #####3. 文件安排 ##二、實現通訊錄的功能 #####1. 添加功能 #####2. 刪除功能 #####3. 展示功能 #####4. 更改功能 #####5. 查找功能 #####6. 排序功能 ## ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...