C++中的stack和queue

C++中的stack和queue

前言

這一節的內容對于stack和queue的使用介紹會比較少,主要是因為stack和queue的使用十分簡單,而且他們的功能主要也是在做題的時候才會顯現。這一欄目暫時不會寫關于做題的內容,后續我會額外開一個做題日記的欄目的。

這節內容主要給大家講解一下stack和queue的特殊性,和相關實現。

使用

在這里插入圖片描述

對stack和queue的簡單介紹

一、基本概念
  1. Stack(棧)

    • 后進先出(LIFO):最后插入的元素最先被移除。
    • 類比:一摞盤子(只能從頂部取放)。
    • 核心操作:push(入棧)、pop(出棧)、top(訪問棧頂)。
  2. Queue(隊列)

    • 先進先出(FIFO):最先插入的元素最先被移除。
    • 類比:排隊購票(隊尾進,隊頭出)。
    • 核心操作:push(入隊)、pop(出隊)、front/back(訪問隊頭/隊尾)。

二、底層實現原理//重點
  • 容器適配器
    stackqueue 不是獨立的容器,而是基于其他容器(如 dequelist)的接口封裝。

    // 默認底層容器:deque(雙端隊列)//這個容器會在下一節講解
    stack<int> s;          // 等價于 stack<int, deque<int>>
    queue<int> q;          // 等價于 queue<int, deque<int>>// 自定義底層容器(需滿足操作要求)
    stack<int, vector<int>> s_vec;   // 使用 vector
    queue<int, list<int>> q_list;    // 使用 list
    

在這里插入圖片描述

  • 底層容器要求

    操作stack 所需支持queue 所需支持
    尾部插入push_back()push_back()
    尾部刪除pop_back()?
    頭部刪除?pop_front()
    訪問尾部back()back()
    訪問頭部?front()

    ?? 注意vector 不能直接用于 queue(缺少 pop_front())。


三、核心操作與時間復雜度
操作stackqueue時間復雜度
插入元素push(val)push(val)O(1)
刪除元素pop()pop()O(1)
訪問頂部/頭部元素top()front()O(1)
訪問尾部元素?back()O(1)
檢查是否為空empty()empty()O(1)
獲取元素數量size()size()O(1)

💡 為什么高效?
默認基于 deque(動態數組分塊存儲),插入/刪除操作均攤常數時間。


四、使用示例
  1. Stack 示例:括號匹配

    #include <stack>
    bool isValid(string s) {stack<char> st;for (char c : s) {if (c == '(' || c == '[') st.push(c);else {if (st.empty()) return false;char top = st.top();if ((c == ')' && top != '(') || (c == ']' && top != '[')) return false;st.pop();}}return st.empty();
    }
    
  2. Queue 示例:BFS 算法框架

    #include <queue>
    void BFS(Node* start) {queue<Node*> q;q.push(start);while (!q.empty()) {Node* cur = q.front();q.pop();// 處理當前節點for (Node* neighbor : cur->neighbors) {q.push(neighbor);}}
    }
    

五、應用場景
數據結構典型應用場景
stack函數調用棧、表達式求值、DFS 遍歷、撤銷操作(Ctrl+Z)
queue消息隊列、BFS 遍歷、緩沖區管理(如打印機任務)、多線程任務調度

六、注意事項
  1. 空容器操作禁止

    stack<int> s;
    s.pop();   // 未定義行為(崩潰)!
    if (!s.empty()) s.pop(); // 正確做法
    
  2. 不支持遍歷
    二者均沒有迭代器,無法用 for (auto x : s) 遍歷。

  3. 底層容器選擇

    • stack 優先選 vector(內存局部性好)。
    • queue 只能選 dequelist(避免用 vector)。
  4. 線程安全
    STL 的 stack/queue 不是線程安全的,多線程需手動加鎖。


七、進階技巧
  1. vector 模擬棧(簡化內存管理):

    vector<int> v_stack;
    v_stack.push_back(10);      // push
    int top = v_stack.back();   // top
    v_stack.pop_back();         // pop
    
  2. 雙棧實現隊列

    class Queue {stack<int> in, out;void push(int x) { in.push(x); }int pop() {if (out.empty()) {while (!in.empty()) {out.push(in.top()); in.pop();}}int val = out.top();out.pop();return val;}
    };
    

總結
特性stackqueue
數據順序LIFO(后進先出)FIFO(先進先出)
核心接口push(), pop(), top()push(), pop(), front()
底層依賴deque/vector/listdeque/list
遍歷不支持不支持
適用場景逆序操作、遞歸順序處理、緩沖

? 選擇原則

  • 需要回溯操作 → stack(如 DFS、撤銷機制)
  • 需要公平調度 → queue(如 BFS、消息隊列)

C++ priority_queue (優先隊列)詳解

一、本質與核心概念

優先隊列(priority_queue) 是一種特殊的隊列,元素按優先級順序出隊(而非插入順序)。它是基于堆數據結構(通常是二叉堆)實現的容器適配器。

也就是說,當我們需要使用到這個數據結構的時候,使用這個優先級隊列就ok了,它也是被包括在queue頭文件中的。

關鍵特性:
  • 隊首元素總是當前隊列中優先級最高的元素
  • 默認行為:最大元素優先(大頂堆//堆頂元素最大)
  • 底層結構:默認使用 vector 作為容器//這個優先級隊列也是一個容器適配器
  • 時間復雜度
    • 訪問隊首:O(1)
    • 插入元素:O(log n)
    • 刪除隊首:O(log n)
二、底層實現原理
template<class T,class Container = std::vector<T>,class Compare = std::less<typename Container::value_type>
> class priority_queue;
  1. 堆結構維護

    • 插入時 (push):將元素添加到末尾,然后進行上浮操作(sift up)//也就是我們學習堆數據結構時候的向上調整。
    • 刪除時 (pop):交換首尾元素,刪除尾部,然后進行下沉操作(sift down)//也就是我們學習堆數據結構時候的向下調整。
  2. 堆算法函數(底層實際調用):

    std::make_heap()   // 建堆
    std::push_heap()   // 插入后調整
    std::pop_heap()    // 刪除前調整
    
三、模板參數詳解
參數說明默認值
T元素類型必填
Container底層容器vector<T>
Compare優先級比較函數less<T>
底層容器要求:
  • 必須支持隨機訪問迭代器(vector/deque
  • 必須支持 front(), push_back(), pop_back()
  • 不兼容 list(缺少隨機訪問)
四、核心操作與時間復雜度
操作函數時間復雜度說明
插入元素push(const T& val)O(log n)添加元素并調整堆
刪除隊首pop()O(log n)移除最高優先級元素
訪問隊首top()O(1)返回隊首元素(只讀)
檢查空empty()O(1)判斷是否為空
獲取大小size()O(1)返回元素數量
五、創建與初始化
// 默認創建(大頂堆)
std::priority_queue<int> max_heap;// 預分配空間
std::vector<int> vec{3,1,4,1,5};
std::priority_queue<int> pre_heap(vec.begin(), vec.end());// 自定義比較函數(小頂堆)
auto cmp = [](int a, int b) { return a > b; };
std::priority_queue<int, std::vector<int>, decltype(cmp)> min_heap(cmp);// 使用函數對象(推薦)//這其實就是仿函數,等會后面會講
struct Compare {bool operator()(const Person& a, const Person& b) {return a.age < b.age; // 年齡大的優先}
};
std::priority_queue<Person, std::vector<Person>, Compare> age_heap;
六、自定義優先級規則(關鍵技巧)
// 方法1:重載 operator<
struct Task {int priority;std::string name;bool operator<(const Task& other) const {return priority < other.priority; // 大頂堆要求}
};// 方法2:自定義函數對象
struct TaskComparator {bool operator()(const Task& a, const Task& b) {return a.priority < b.priority; // 注意:返回true表示a優先級低于b}
};// 方法3:使用標準函數對象(創建小頂堆)
std::priority_queue<int, std::vector<int>, std::greater<int>> min_heap;

?? 比較函數重要規則

  • comp(a, b) == true 時,a 的優先級將低于 b
  • 對于大頂堆:return a < b;
  • 對于小頂堆:return a > b;
七、典型應用場景
  1. 任務調度系統

    struct Task {int priority;std::function<void()> job;bool operator<(const Task& t) const { return priority < t.priority; }
    };
    std::priority_queue<Task> scheduler;
    
  2. Dijkstra 最短路徑算法

    std::priority_queue<std::pair<int, int>> pq; // { -distance, node }
    pq.push({0, start}); // 使用負值實現最小堆
    
  3. 合并K個有序鏈表

    auto cmp = [](ListNode* a, ListNode* b) { return a->val > b->val; 
    };
    std::priority_queue<ListNode*, std::vector<ListNode*>, decltype(cmp)> pq(cmp);
    
  4. 實時獲取中位數(雙堆法):

    std::priority_queue<int> max_heap; // 存儲較小一半
    std::priority_queue<int, std::vector<int>, std::greater<int>> min_heap; // 存儲較大一半
    
八、底層容器選擇策略
容器隨機訪問內存連續性適用性推薦場景
vector??推薦通用場景
deque??(分塊)可用避免尾部擴容
list??不兼容-

為什么用 vector 不用 deque

  • vector 內存連續,緩存友好
  • 堆操作主要依賴隨機訪問,vector 性能更優
九、特殊操作與技巧
  1. 高效初始化

    std::vector<int> data{3,1,4,5,9,2,6};
    // 直接通過迭代器建堆(O(n) 時間復雜度)
    std::priority_queue<int> pq(data.begin(), data.end());
    
  2. 清空隊列的陷阱

    // 錯誤方式(低效):
    while (!pq.empty()) pq.pop(); // O(n log n)// 正確方式:
    std::priority_queue<int> empty;
    std::swap(pq, empty); // O(1)
    
  3. 修改隊首元素(高級技巧)

    // 1. 取出隊首
    auto top = pq.top();
    pq.pop();// 2. 修改元素
    top.value = new_value;// 3. 重新插入
    pq.push(top);
    
十、與相似數據結構的對比
特性priority_queueset/multisetmap/multimap
排序方式堆序紅黑樹序紅黑樹序
重復元素允許multiset允許multimap允許
隨機訪問僅隊首不支持不支持
插入刪除O(log n)O(log n)O(log n)
查找元素不支持O(log n)O(log n)
內存開銷高(指針)高(指針)
十一、性能優化指南
  1. 元素使用移動語義

    struct HeavyObject {std::vector<int> big_data;HeavyObject(HeavyObject&& other) noexcept = default;
    };
    pq.push(std::move(obj)); // 避免大對象復制
    
  2. 預留空間(僅 vector 底層)

    // 直接訪問底層容器(不推薦但有效)
    std::priority_queue<int, std::vector<int>> pq;
    pq.c.reserve(1000); // 訪問底層容器的reserve
    
  3. 自定義內存分配器

    template<typename T>
    class CustomAllocator { /* ... */ };using CustomPQ = std::priority_queue<int, std::vector<int, CustomAllocator<int>>;
    
十二、常見錯誤與陷阱
  1. 錯誤比較函數

    // 錯誤:返回true表示a優先級低于b
    auto cmp = [](int a, int b) { return a < b; }; // 正確的大頂堆
    auto wrong_cmp = [](int a, int b) { return a > b; }; // 錯誤!實際創建了小頂堆
    
  2. 修改隊首破壞堆結構

    // 錯誤:直接修改隊首元素
    pq.top().priority = 100; // 破壞堆性質!
    
  3. 遍歷隊列陷阱

    while (!pq.empty()) {process(pq.top()); // 錯誤:忘記pop()導致死循環pq.pop(); // 必須顯式pop
    }
    
十三、最佳實踐總結
場景推薦方案
需要最高優先級元素priority_queue
需要完整排序setmap
需要遍歷元素vector + std::sort
需要快速查找unordered_map
內存敏感場景priority_queue + vector

? 黃金法則

  1. 優先使用 vector 作為底層容器
  2. 比較函數中明確優先級規則
  3. 避免直接修改隊列內部元素
  4. 批量初始化優于逐個插入
  5. 使用移動語義減少拷貝開銷
十四、C++20 新特性
  1. 范圍構造支持

    std::vector data{5, 3, 8, 1};
    std::priority_queue pq(std::less{}, data); // C++20 類型推導
    
  2. 透明比較器優化

    std::priority_queue<std::string, std::vector<std::string>,std::less<>> pq; // 支持異構查找
    pq.push("test");
    auto it = find_if(begin(pq.c), end(pq.c), [](auto& s){ return s == "test"; }); // 直接比較
    
十五、綜合示例:醫院急診系統
struct Patient {std::string name;int severity; // 1-10, 10為最嚴重time_t arrival_time;bool operator<(const Patient& other) const {// 優先級規則:先按嚴重程度,再按到達時間return std::tie(severity, arrival_time) < std::tie(other.severity, other.arrival_time);}
};std::priority_queue<Patient> emergency_queue;void admit_patient(std::string name, int severity) {emergency_queue.push({name, severity, time(nullptr)});
}void treat_next_patient() {if (emergency_queue.empty()) return;Patient next = emergency_queue.top();emergency_queue.pop();std::cout << "Treating: " << next.name << " (Severity: " << next.severity << ")\n";
}
總結:優先隊列三要素
  1. 數據結構核心:二叉堆實現
  2. 優先級控制:通過比較函數定制
  3. 性能平衡
    • O(1) 訪問隊首
    • O(log n) 插入/刪除
    • O(n) 建堆

📌 使用決策樹
需要不斷獲取 最大值/最小值 + 頻繁插入刪除 → 選擇 priority_queue
需要完整排序/遍歷 → 選擇 set 或排序 vector
需要鍵值關聯 → 選擇 map

容器適配器

那么我們這一節的重點其實就是這個知識點:容器適配器。弄清楚容器適配器是啥?怎么使用。

一、什么是容器適配器?

容器適配器(Container Adapter)是 STL 中的特殊容器,它們不直接管理數據,而是通過"借用"已有容器(如 deque、vector、list)的功能,提供特定接口來實現特定數據結構行為。

類比解釋:

這里我結合stack和現實生活中的樣例給大家解釋一下:

stack(也就是棧)它的特點不就是后進先出嘛,那么要怎么實現后進先出呢?那其實也很簡單,首先棧的插入,也就是棧的push功能,其實就是尾部插入數據嘛。那么后進先出,這里其實涉及到的就是pop刪除元素的功能函數嘛,后進先出,也就是pop元素的時候默認調用一個尾部刪除pop_back函數。棧的一些其余功能,如取棧頂元素是返回數組末尾元素。之前在講棧的時候就說過棧其實是一個特殊的線性表,特殊在它的操作(如刪除操作就是指定尾部刪除數據)。但是這些特殊操作我們也可以調用普通數據結構中的某些函數來實現,就好比如vector就可以完美的轉換成stack。之前我們學習數據結構的時候,因為沒有模板,所以我們要額外寫一份棧的代碼,但是現在有了模板之后,我們就可以借助模板,將某些容器轉換成某些別的容器,就好比如vector轉換成stack。

用生活中的例子給大家解釋的話,我覺得大家可以這樣想:假設現在電腦上沒有網線接口或者有線耳機的線孔,只有一個Type-C接口,那么這個Type-C接口我們就可以把它當作vector。現在網上不是有很多擴展塢嘛,擴展塢上啥孔都有,我們把這個擴展塢想象成模板。假設我現在想給我的筆記本電腦插上網線,但是沒有網線接口。那么我們就可以使用擴展塢將原來的Type-C接口變成網線接口。在C++中,vector轉換成stack是容器轉容器,在現實里,Type-C接口轉網線接口是接口轉接口。大家可以這樣類比的理解,雖然這是一個不太嚴謹的例子,但是我個人覺得還是不錯的。

所以說這里的網線其實是借用Type-C接口進行與電腦的連接。

二、為什么需要容器適配器?
方式直接實現數據結構使用容器適配器
開發成本需從頭實現所有操作復用已有容器代碼
靈活性固定實現可自由切換底層容器
維護性需單獨維護依賴 STL 標準容器
性能優化優化困難輕松切換高效底層容器
三、三大容器適配器
  1. stack:后進先出(LIFO)結構
  2. queue:先進先出(FIFO)結構
  3. priority_queue:優先級隊列(元素按優先級排序)
四、底層容器要求(核心知識)

每個適配器對底層容器有特定接口要求:

1. stack 的底層容器必須支持:
push_back()  // 尾部插入
pop_back()   // 尾部刪除
back()       // 訪問尾部元素

? 兼容容器:deque(默認)、vectorlist

2. queue 的底層容器必須支持:
push_back()  // 尾部插入
pop_front()  // 頭部刪除
front()      // 訪問頭部元素
back()       // 訪問尾部元素(非必須但常用)

? 兼容容器:deque(默認)、list
? 不兼容:vector(缺少 pop_front)

3. priority_queue 的額外要求:
// 需要隨機訪問迭代器
front()             // 訪問首元素
push_back()         // 尾部插入
pop_back()          // 尾部刪除
// 內部使用堆算法(需支持隨機訪問)

? 兼容容器:vector(默認)、deque

五、底層容器性能對比
操作vectordequelist
尾部插入O(1) 均攤O(1)O(1)
頭部插入O(n)O(1)O(1)
隨機訪問O(1)O(1)O(n)
內存布局連續內存分塊連續非連續
適配 stack? 推薦??
適配 queue? 不兼容? 默認?

💡 黃金搭配

  • stack + vector:內存連續,緩存友好
  • queue + deque:高效頭尾操作
  • priority_queue + vector:堆算法需要隨機訪問
六、自定義底層容器(實戰示例)
#include <stack>
#include <vector>
#include <list>
#include <queue>// 自定義stack底層容器
std::stack<int, std::vector<int>> vec_stack;  // 基于vector
std::stack<int, std::list<int>> list_stack;    // 基于list// 自定義queue底層容器
std::queue<int, std::list<int>> list_queue;    // 基于list// priority_queue使用vector
std::priority_queue<int, std::vector<int>> max_heap;
七、容器適配器 VS 普通容器
特性普通容器(vector/list)容器適配器(stack/queue)
迭代器? 支持完整迭代器? 無迭代器
遍歷? 可隨機訪問任意元素? 只能訪問特定端元素
底層數據? 可直接訪問? 隱藏底層實現
功能接口提供完整容器操作僅暴露特定數據結構接口
設計目的通用數據存儲特定數據行為抽象
八、實現原理揭秘
template<typename T, typename Container = std::deque<T>>
class stack //棧
{public:void push(const T& value) { c.push_back(value);  // 調用底層容器的push_back}void pop() { c.pop_back();        // 調用底層容器的pop_back}T& top() { return c.back();     // 調用底層容器的back}size_t size(){return c.size();//調用底層容器的size}bool empty(){return c.empty();//調用底層容器的empty}private:Container c;  // 底層容器對象
};
template<class T, class Container = std::deque<T>>class queue//隊列{public:void push(const T& x){_con.push_back(x);}void pop(){_con.pop_front();}const T& front(){return _con.front();}const T& back(){return _con.back();}size_t size(){return _con.size();}bool empty(){return _con.empty();}private:Container _con;};
#include <vector>
#include <functional> // 用于 std::less
#include <algorithm> // 用于 std::swaptemplate <typename T, typename Container = std::vector<T>,typename Compare = std::less<typename Container::value_type>>
class priority_queue //優先隊列
{
public:// 默認構造函數priority_queue() = default;// 使用自定義比較器的構造函數explicit priority_queue(const Compare& comp) : comp(comp) {}// 通過迭代器范圍構造template <typename InputIterator>priority_queue(InputIterator first, InputIterator last, const Compare& comp = Compare()): c(first, last), comp(comp) {build_heap(); // 構建堆}// 插入元素void push(const T& value) {c.push_back(value);       // 添加到末尾sift_up(c.size() - 1);    // 上浮新元素}// 刪除堆頂元素void pop() {if (c.empty()) return;std::swap(c[0], c.back()); // 交換首尾元素c.pop_back();              // 刪除原堆頂if (!c.empty()) {sift_down(0);          // 下沉新堆頂}}// 訪問堆頂元素(只讀)const T& top() const {return c.front();}// 元素數量size_t size() const {return c.size();}// 檢查是否為空bool empty() const {return c.empty();}private:Container c;     // 底層容器Compare comp;    // 比較函數對象// 構建堆(Floyd算法)void build_heap() {// 從最后一個非葉子節點開始下沉for (int i = (c.size() / 2) - 1; i >= 0; --i) {sift_down(i);}}// 上浮操作void sift_up(size_t idx) {while (idx > 0) {size_t parent = (idx - 1) / 2;// 如果當前節點優先級高于父節點if (comp(c[parent], c[idx])) {std::swap(c[parent], c[idx]);idx = parent;} else {break; // 堆條件已滿足}}}// 下沉操作void sift_down(size_t idx) {size_t largest = idx;const size_t n = c.size();while (true) {size_t left = 2 * idx + 1;size_t right = 2 * idx + 2;// 檢查左子節點if (left < n && comp(c[largest], c[left])) {largest = left;}// 檢查右子節點if (right < n && comp(c[largest], c[right])) {largest = right;}// 如果當前節點不是最大,交換并繼續下沉if (largest != idx) {std::swap(c[idx], c[largest]);idx = largest;} else {break; // 堆條件已滿足}}}
};

關鍵點:所有操作轉發到底層容器的特定接口

九、特殊行為解析
  1. 為什么 stack 不提供 clear()
    因為標準委員會認為 while(!s.empty()) s.pop() 已足夠清晰

  2. priority_queue 的排序控制

    // 創建小頂堆(默認是大頂堆)
    std::priority_queue<int, std::vector<int>, std::greater<int>> min_heap;
    
  3. queueback() 不是必須的
    某些實現(如早期 STL)允許使用不支持 back() 的容器

十、設計思想與最佳實踐
  1. 適配器模式精髓

    • 不改變已有容器代碼
    • 通過限制接口實現新功能
    • 符合開閉原則(對擴展開放,對修改封閉)
  2. 使用建議

    // 需要后進先出 → 選擇 stack
    stack<int, vector<int>> s;  // 推薦 vector 提高緩存命中率// 需要先進先出 → 選擇 queue
    queue<int> q;               // 默認 deque 最均衡// 需要優先級處理 → priority_queue
    priority_queue<int> pq;     // 默認 vector 大頂堆
    
  3. 避免的陷阱

    // 錯誤:vector 缺少 pop_front()
    std::queue<int, std::vector<int>> invalid_queue; // 錯誤:嘗試遍歷適配器
    for(auto it = s.begin(); it != s.end(); ++it) // stack 無迭代器
    
十一、性能優化技巧
  1. 預分配內存(vector 底層時)

    std::stack<int, std::vector<int>> s;
    s.c.reserve(1000);  // 直接訪問底層容器預分配
    
  2. 選擇分塊存儲容器避免擴容

    // 處理超大數據時使用 deque
    std::stack<int, std::deque<int>> big_stack;
    
  3. 自定義內存分配器

    std::stack<int, std::vector<int, MyAllocator<int>>> custom_stack;
    
十二、總結:容器適配器的本質
維度說明
身份底層容器的"接口轉換器"
核心能力通過限制/轉發底層容器接口實現特定數據結構
價值代碼復用 + 接口統一 + 靈活切換
哲學思想“不要重復發明輪子”(Don’t Reinvent The Wheel)
典型應用算法中的臨時數據結構(DFS/BFS)、消息系統、撤銷操作棧

? 使用口訣
棧用 vector,隊列用 deque
優先隊列 vector,底層切換要合規
迭代遍歷不可為,專用接口記心間!

仿函數

一、什么是仿函數?

仿函數(Function Object)是C++中行為類似函數的對象。它們通過重載函數調用運算符operator()實現,使得對象可以像函數一樣被調用。

基本特征:

  • 是類對象,不是函數
  • 重載了operator()運算符
  • 可以保存狀態(成員變量)
  • 可以作為參數傳遞
  • 可以像普通函數一樣調用

簡單示例:

#include <iostream>// 定義一個加法仿函數
struct Adder {int operator()(int a, int b) const {return a + b;}
};int main() {Adder add; // 創建仿函數對象std::cout << add(3, 4); // 像函數一樣調用:輸出7return 0;
}

二、為什么需要仿函數?

1. 相比普通函數的優勢:

特性普通函數仿函數
狀態保持? 無狀態? 可保存狀態
內聯優化? 通常不能? 可內聯
作為模板參數? 不支持? 支持
多態行為? 不支持? 支持

2. 實際應用場景:

  • STL算法的自定義行為(如排序、查找)
  • 回調機制
  • 策略模式實現
  • 函數組合

三、仿函數的類型分類

1. 按參數數量分類:

類型參數數量示例
生成器(Generator)0rand()
一元仿函數(Unary)1negate<T>()
二元仿函數(Binary)2plus<T>()

2. 按行為分類:

類型返回值用途示例
算術仿函數數值數學運算plus, minus
關系仿函數bool比較操作less, greater
邏輯仿函數bool邏輯運算logical_and, logical_or

四、STL內置仿函數詳解

STL在<functional>頭文件中提供了常用仿函數:

1. 算術仿函數:

#include <functional>std::plus<int> add;
int result = add(5, 3); // 8std::minus<int> subtract;
result = subtract(10, 4); // 6std::multiplies<int> multiply;
result = multiply(3, 4); // 12std::divides<int> divide;
result = divide(10, 2); // 5std::modulus<int> mod;
result = mod(10, 3); // 1std::negate<int> neg;
result = neg(7); // -7

2. 關系仿函數:

std::equal_to<int> equal;
bool isEqual = equal(5, 5); // truestd::not_equal_to<int> notEqual;
isEqual = notEqual(5, 3); // truestd::greater<int> greater;
isEqual = greater(5, 3); // truestd::less<int> less;
isEqual = less(3, 5); // truestd::greater_equal<int> ge;
isEqual = ge(5, 5); // truestd::less_equal<int> le;
isEqual = le(4, 5); // true

3. 邏輯仿函數:

std::logical_and<bool> and_op;
bool res = and_op(true, false); // falsestd::logical_or<bool> or_op;
res = or_op(true, false); // truestd::logical_not<bool> not_op;
res = not_op(true); // false

五、自定義仿函數實現

1. 基本實現:

// 冪運算仿函數
class Power {
public:double operator()(double base, int exponent) const {double result = 1.0;for (int i = 0; i < exponent; ++i) {result *= base;}return result;}
};// 使用
Power power;
double eight = power(2, 3); // 8.0

2. 帶狀態的仿函數:

// 計數器仿函數
class Counter {int count = 0;
public:int operator()() { return ++count; }void reset() {count = 0;}
};// 使用
Counter c;
c(); // 1
c(); // 2
c.reset();
c(); // 1

3. 模板仿函數:

template <typename T>
class Clamp {T min_val;T max_val;
public:Clamp(T min, T max) : min_val(min), max_val(max) {}T operator()(T value) const {if (value < min_val) return min_val;if (value > max_val) return max_val;return value;}
};// 使用
Clamp<int> intClamp(0, 100);
int value = intClamp(150); // 100Clamp<double> doubleClamp(0.0, 1.0);
double dval = doubleClamp(-0.5); // 0.0

六、仿函數在STL算法中的應用

1. 排序算法:

#include <algorithm>
#include <vector>std::vector<int> numbers = {5, 3, 8, 1, 4};// 升序排序
std::sort(numbers.begin(), numbers.end(), std::less<int>());// 降序排序
std::sort(numbers.begin(), numbers.end(), std::greater<int>());

2. 查找算法:

// 查找第一個大于5的元素
auto it = std::find_if(numbers.begin(), numbers.end(), [](int x) { return x > 5; });// 使用仿函數替代lambda
struct GreaterThan {int threshold;GreaterThan(int t) : threshold(t) {}bool operator()(int x) const { return x > threshold; }
};it = std::find_if(numbers.begin(), numbers.end(), GreaterThan(5));

3. 轉換算法:

#include <vector>
#include <cmath>struct SquareRoot {double operator()(double x) const {return std::sqrt(x);}
};std::vector<double> values = {1.0, 4.0, 9.0, 16.0};
std::vector<double> roots;// 計算平方根
std::transform(values.begin(), values.end(), std::back_inserter(roots), SquareRoot());

七、仿函數適配器

1. 綁定器(Binder):

#include <functional>
#include <algorithm>using namespace std::placeholders; // 用于 _1, _2 等占位符// 綁定第一個參數為10
auto greaterThan10 = std::bind(std::greater<int>(), _1, 10);std::vector<int> data = {5, 12, 8, 15, 3};
int count = std::count_if(data.begin(), data.end(), greaterThan10); // 2

2. 取反器(Negator):

// 創建不等于5的謂詞
auto notEqual5 = std::not1(std::bind2nd(std::equal_to<int>(), 5));count = std::count_if(data.begin(), data.end(), notEqual5); // 4

八、仿函數 vs Lambda表達式

比較:

特性仿函數Lambda表達式
語法簡潔性? 需要完整類定義? 簡潔
狀態管理? 顯式成員變量? 捕獲列表
復用性? 可多次使用? 通常一次性
模板支持? 完全支持?? C++20起支持
調試? 更容易? 匿名類型

轉換示例:

// Lambda表達式
auto lambda = [](int a, int b) { return a * b; };// 等效仿函數
class Multiplier {
public:int operator()(int a, int b) const {return a * b;}
};

九、高級技巧與最佳實踐

1. 函數組合:

template <typename F, typename G>
class Compose {F f;G g;
public:Compose(F f, G g) : f(f), g(g) {}template <typename T>auto operator()(T x) const -> decltype(f(g(x))) {return f(g(x));}
};// 使用:先平方再取負
Compose<std::negate<double>, std::multiplies<double>> squareAndNegate(std::negate<double>(), std::multiplies<double>());double result = squareAndNegate(4.0); // -16.0

2. 性能優化:

  • 聲明operator()inline
  • 對于簡單操作,使用空仿函數(無狀態)
  • 優先傳遞仿函數對象而非函數指針

3. 現代C++改進(C++11/14/17/20):

  • 自動類型推導auto關鍵字簡化聲明
  • 泛型Lambda(C++14):
    auto genericAdder = [](auto a, auto b) { return a + b; };
    
  • 模板Lambda(C++20):
    auto templated = []<typename T>(T a, T b) { return a + b; };
    

十、實際應用案例

1. 自定義排序:

struct Person {std::string name;int age;double salary;
};// 多級排序仿函數
class PersonSorter {
public:bool operator()(const Person& a, const Person& b) const {// 先按年齡升序if (a.age != b.age) return a.age < b.age;// 年齡相同按薪資降序if (a.salary != b.salary)return a.salary > b.salary;// 最后按名字升序return a.name < b.name;}
};std::vector<Person> people;
std::sort(people.begin(), people.end(), PersonSorter());

2. 策略模式實現:

// 支付策略接口
class PaymentStrategy {
public:virtual double calculate(double amount) const = 0;virtual ~PaymentStrategy() = default;
};// 信用卡支付策略
class CreditCardStrategy : public PaymentStrategy {double feeRate;
public:CreditCardStrategy(double rate) : feeRate(rate) {}double calculate(double amount) const override {return amount * (1 + feeRate);}
};// 支付處理器
class PaymentProcessor {PaymentStrategy* strategy;
public:void setStrategy(PaymentStrategy* s) { strategy = s; }double processPayment(double amount) {return strategy->calculate(amount);}
};

總結:仿函數的優勢與選擇

何時使用仿函數:

  1. 需要保持狀態或多次復用
  2. 作為模板參數傳遞
  3. 需要復雜的行為或策略
  4. 需要多態行為
  5. 性能敏感場景(內聯優化)

何時使用Lambda:

  1. 簡單、一次性操作
  2. 需要捕獲局部變量
  3. 代碼簡潔性優先
  4. C++11及以上環境

仿函數設計原則:

  1. 保持operator()const(除非需要修改狀態)
  2. 提供清晰的命名
  3. 確保無副作用(或明確文檔說明)
  4. 對于模板仿函數,提供類型約束(C++20概念)

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/90459.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/90459.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/90459.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Spring Bean生命周期七步曲:定義、實例化、初始化、使用、銷毀

各位小猿&#xff0c;程序員小猿開發筆記&#xff0c;希望大家共同進步。 引言 1.整體流程圖 2.各階段分析 1??定義階段 1.1 定位資源 Spring 掃描 Component、Service、Controller 等注解的類或解析 XML/Java Config 中的 Bean 定義 1.2定義 BeanDefinition 解析類信息…

API安全監測工具:數字經濟的免疫哨兵

&#x1f4a5; 企業的三重致命威脅 1. 漏洞潛伏的定時炸彈 某支付平臺未檢測出API的批量數據泄露漏洞&#xff0c;導致230萬用戶信息被盜&#xff0c;面臨GDPR 1.8億歐元罰單&#xff08;IBM X-Force 2024報告&#xff09;。傳統掃描器對邏輯漏洞漏檢率超40%&#xff08;OWASP基…

Matplotlib詳細教程(基礎介紹,參數調整,繪圖教程)

目錄 一、初識Matploblib 1.1 安裝 Matplotlib 1.2、Matplotlib 的兩種接口風格 1.3、Figure 和 Axes 的深度理解 1.4 設置畫布大小 1.5 設置網格線 1.6 設置坐標軸 1.7 設置刻度和標簽 1.8 添加圖例和標題 1.9 設置中文顯示 1.10 調整子圖布局 二、常用繪圖教程 2…

Redis高可用架構演進面試筆記

1. 主從復制架構 核心概念Redis單節點并發能力有限&#xff0c;通過主從集群實現讀寫分離提升性能&#xff1a; Master節點&#xff1a;負責寫操作Slave節點&#xff1a;負責讀操作&#xff0c;從主節點同步數據 主從同步流程 全量同步&#xff08;首次同步&#xff09;建立連接…

無人機保養指南

定期清潔無人機在使用后容易積累灰塵、沙礫等雜物&#xff0c;需及時清潔。使用軟毛刷或壓縮空氣清除電機、螺旋槳和機身縫隙中的雜質。避免使用濕布直接擦拭電子元件&#xff0c;防止短路。電池維護鋰電池是無人機的核心部件&#xff0c;需避免過度放電或充電。長期存放時應保…

vlm MiniCPM 學習部署實戰

目錄 開源地址&#xff1a; 模型repo下載&#xff1a; 單圖片demo&#xff1a; 多圖推理demo&#xff1a; 論文學習筆記&#xff1a; 部署完整教程&#xff1a; 微調教程&#xff1a; 部署&#xff0c;微調教程&#xff0c;視頻實測 BitCPM4 技術報告 創意&#xff1…

92套畢業相冊PPT模版

致青春某大學同學聚會PPT模版&#xff0c;那些年我們一起走過的歲月PPT模版&#xff0c;某學院某班同學聯誼會PPT模版&#xff0c;匆匆那年PPT模版&#xff0c;青春的紀念冊PPT模版&#xff0c;梔子花開PPT模版&#xff0c;畢業紀念冊PPT模版。 92套畢業相冊PPT模版&#xff1…

爬蟲基礎概念

網絡爬蟲概述 概念 網絡爬蟲&#xff08;Web Crawler&#xff09;&#xff0c;也稱為網絡蜘蛛&#xff08;Web Spider&#xff09;或機器人&#xff08;Bot&#xff09;&#xff0c;是一種自動化程序&#xff0c;用于系統地瀏覽互聯網并收集網頁信息。它模擬人類瀏覽器行為&…

java8 stream流操作的flatMap

我們來詳細解釋一下 Java 8 Stream API 中的 flatMap 操作。理解 flatMap 的關鍵在于將其與 map 操作進行對比。??核心概念&#xff1a;????map 操作&#xff1a;??作用&#xff1a;將一個流中的每個元素??轉換??為另一個元素&#xff08;類型可以不同&#xff09;…

開源UI生態掘金:從Ant Design二次開發到行業專屬組件的技術變現

開源UI生態掘金&#xff1a;從Ant Design二次開發到行業專屬組件的技術變現內容摘要在開源UI生態中&#xff0c;Ant Design作為一款廣受歡迎的UI框架&#xff0c;為開發者提供了強大的基礎組件。然而&#xff0c;面對不同行業的特定需求&#xff0c;僅僅依靠現有的組件往往難以…

Object Sense (OSE):一款從編輯器腳本發展起來的編程語言

引言&#xff1a;從Vim編輯器走出的語言在編程語言的世界里&#xff0c;許多革命性的創新往往源于看似簡單的工具。Object Sense&#xff08;簡稱OSE&#xff09;的誕生&#xff0c;便與一款經典文本編輯器——Vim息息相關。它的前身是Vim的腳本語言VimL&#xff08;Vimscript&…

我考PostgreSQL中級專家證書二三事

1. 為什么選擇PGCE&#xff1f;PostgreSQL的開源特性、高性能和高擴展性早已讓我心生向往&#xff0c;而PGCE認證不僅是對技術能力的認可&#xff0c;更是一張通往更高職業舞臺的“通行證”。官方資料提到&#xff0c;PGCE考試涵蓋性能優化、高可用架構、復雜查詢處理、內核原理…

Java 動態導出 Word 登記表:多人員、分頁、動態表格的最佳實踐

本文詳細講解如何使用 Java 動態導出包含多人員報名表的 Word 文檔&#xff0c;每人占據獨立一頁&#xff0c;并支持動態表格行&#xff08;如個人經歷&#xff09;。我們對比了多種實現方案&#xff0c;最終推薦基于 Freemarker XML 模板 或 docx4j 的靈活方式&#xff0c;并…

【element-ui el-table】多選表格勾選時默認勾選了全部,row-key綁定異常問題解決

項目場景&#xff1a; Element-UI的el-table組件row-key使用問題 同一個頁面使用了幾個table&#xff0c;這幾個table都使用了多選&#xff0c;row-key屬性&#xff0c;其中row-key的綁定方式都是用的靜態綁定&#xff0c;row-key“username”或row-key“id”&#xff0c;可正常…

C#注釋技巧與基礎編程示例

以下是一個包含基礎注釋的 C# 程序示例&#xff0c;展示了 C# 中各類注釋的使用方法&#xff1a;using System;namespace BasicCSharpProgram {/// <summary>/// Program 類是應用程序的入口點/// 包含 Main 方法作為程序執行的起點/// </summary>public class Pro…

極客大挑戰2019-HTTP

涵蓋知識&#xff1a;UA頭偽造漏洞&#xff1a;全稱&#xff1a;User-Agent 這個部分包含我們所使用的操作系統版本&#xff0c;cpu&#xff0c;瀏覽器類型等。來源偽造漏洞&#xff1a;在http請求頭中會攜帶一個Referer&#xff0c;這個用來表示服務器用戶是從哪個地方來的X-F…

談談ArrayList與Vector的理解?

目錄 擴容機制 ArrayList擴容源碼 Vector擴容源碼 二者區別 擴展&#xff1a;stack(棧&#xff09; 1.創建stack對象 2. 入棧(先進后出&#xff09; 3.出棧 擴展&#xff1a;舉個例子&#xff1a;實現下字符串逆置&#xff0c;利用stack棧來實現。 從接口實現上&#xff…

【Linux庖丁解牛】— 多線程同步 !

1. 什么是線程同步為什么會有線程同步&#xff0c;那一定是有了新問題。互斥可以解決臨界資源被同時訪問的問題&#xff0c;但是純互斥也會帶來新的問題。由于當前被執行的線程離cpu最近【其他線程被阻塞掛起還要被喚醒】&#xff0c;所以&#xff0c;當前進程對于競爭鎖天然就…

基于arduino uno r3主控的環境監測系統設計-1

準備設計arduino uno r3為主控的環境監測系統&#xff0c;通過傳感器采集TVOC&#xff08;總揮發性有機物&#xff09;、HCHO&#xff08;甲醛&#xff09;和eCO2&#xff08;等效二氧化碳&#xff09;數據&#xff0c;并顯示在LCD屏幕上&#xff0c;同時支持數據記錄到SD卡&am…

ITIL 4:云計算與微服務對組織架構的影響

這幾年&#xff0c;很多組織在推進數字化轉型時遇到一個共同的問題&#xff1a;業務節奏越來越快&#xff0c;但內部協作的“架構”卻越來越跟不上節奏。技術架構的變革&#xff0c;必須同步推動組織架構的重塑。特別是隨著云計算和微服務架構的廣泛應用&#xff0c;這種影響愈…