【項目】 :C++ - 仿mudou庫one thread one loop式并發服務器實現(模塊劃分)

【項目】 :C++ - 仿mudou庫one thread one loop式并發服務器實現

  • 一、HTTP 服務器與 Reactor 模型
    • 1.1、HTTP 服務器
      • 概念
      • 實現步驟
      • 難點
    • 1.2、Reactor 模型
      • 概念
      • 分類
        • 1. 單 Reactor 單線程
        • 2. 單 Reactor 多線程
        • 3. 多 Reactor 多線程
      • 目標定位
    • 總結
  • 二、功能模塊劃分
    • 2.1、SERVER 模塊
      • 子模塊
        • Buffer 模塊
        • Socket 模塊
        • Channel 模塊
        • Connection 模塊
        • Acceptor 模塊
        • TimerQueue 模塊
        • Poller 模塊
        • EventLoop 模塊
        • TcpServer 模塊
      • 模塊關系圖:
    • 2.2、HTTP 協議模塊
      • 模塊劃分
        • **Util 模塊**(工具模塊)
        • **HttpRequest 模塊**(HTTP 請求數據模塊)
        • **HttpResponse 模塊**(HTTP 響應數據模塊)
        • **HttpContext 模塊**(HTTP 上下文模塊)
        • **HttpServer 模塊**(HTTP 服務器模塊)
  • 三、C++11 技術點與功能用例
    • 3.1 bind 概念
      • 示例 1:綁定固定參數或預留參數
      • 示例 2:任務池中的應用
      • 總結
    • 3.2 Linux 定時器 timerfd
      • 創建定時器
      • 設置定時器時間
      • 示例:每隔 3 秒觸發一次
    • 3.3 時間輪思想
      • 問題
      • 原理
      • 延遲任務 + 智能指針
      • 定時任務類設計
      • 時間輪實現
      • 使用示例
      • 總結
    • 3.4 C++ 正則庫(<regex>)的簡單使用
      • HTTP 請求首行解析
      • 完整示例
      • 總結
    • 3.5 C++ 通用類型 Any 的實現與使用
      • Any 類型的簡單實現
      • 測試Any類型
      • C++17 中的 std::any 使用

??通過實現的高并發服務器組件,可以簡潔快速的完成一個高性能的服務器搭建。并且,通過組件內提供的不同應用層協議支持,也可以快速完成一個高性能應用服務器的搭建(當前為了便于項目的演示,項目中提供HTTP協議組件的支持)。在這里,要明確的是咱們要實現的是一個高并發服務器組件,因此當前的項目中并不包含實際的業務內容。

代碼倉庫:https://gitee.com/rxrw/server

一、HTTP 服務器與 Reactor 模型


1.1、HTTP 服務器

概念

HTTP(Hyper Text Transfer Protocol,超文本傳輸協議)是應用層協議,屬于 請求-響應協議

  • 客戶端發送請求,服務器提供服務,完成后關閉連接。
  • HTTP 協議運行在 TCP 協議之上

因此,HTTP 服務器本質上就是 TCP 服務器,只是在應用層基于 HTTP 協議格式進行數據組織和解析來完成業務處理。

實現步驟

  1. 搭建一個 TCP 服務器,接收客戶端請求。
  2. 按照 HTTP 協議格式解析請求數據,明確客戶端目的。
  3. 根據客戶端請求提供對應服務。
  4. 將服務結果按照 HTTP 協議格式組織并返回給客戶端。

難點

  • 實現一個簡單 HTTP 服務器并不復雜。
  • 難點在于如何實現高性能的服務器
  • 本單元將基于 Reactor 模式 實現高性能服務器。

目標:構建一個 高性能服務器基礎庫,作為通用組件,而不是具體的業務服務器。


1.2、Reactor 模型

概念

Reactor 模式是一種 事件驅動處理模式

  • 通過 I/O 多路復用 統一監聽事件。
  • 當事件觸發時,將其分發(Dispatch)給對應的處理線程執行。
  • 又稱 Dispatcher 模式

這是編寫高性能網絡服務器的核心技術之一。

分類

1. 單 Reactor 單線程
  • 模型特點:單 I/O 多路復用 + 業務處理(同線程完成)
  • 流程
    1. I/O 多路復用監控客戶端請求。
    2. 事件觸發:
      • 新連接 → 加入多路復用監控。
      • 數據通信 → 讀數據 → 處理 → 響應。
  • 優點:實現簡單,無線程間通信。
  • 缺點:無法利用多核 CPU,性能瓶頸明顯。
  • 適用場景:少量客戶端,快速處理場景。
2. 單 Reactor 多線程
  • 模型特點:單 I/O 多路復用 + 線程池(業務處理)
  • 流程
    1. Reactor 線程監控請求。
    2. 新連接 → Reactor 處理并加入監控。
    3. 數據通信 → Reactor 讀數據 → 分發給 Worker 線程池。
    4. Worker 處理完成 → Reactor 返回響應。
  • 優點:利用多核 CPU。
  • 缺點:多線程同步復雜,Reactor 本身可能成為瓶頸。
3. 多 Reactor 多線程
  • 模型特點:主從 Reactor 分工 + 線程池
  • 流程
    1. 主 Reactor:只處理新連接請求,并分發給子 Reactor。
    2. 子 Reactor:監控通信事件。
    3. Worker 線程池:處理業務邏輯,并由子 Reactor 返回結果。
  • 優點:充分利用多核 CPU,主從職責清晰,性能強大。
  • 缺點:設計復雜。

目標定位

  • One Thread One Loop 主從 Reactor 模型
    • 主 Reactor 僅監控監聽套接字,負責高效接入新連接。
    • 子 Reactor 負責通信事件處理。
    • 每個線程綁定一個 EventLoop,保證線程安全。
  • Worker 線程池是否使用,由組件調用方決定。

總結

  • HTTP 服務器的核心是 基于 TCP + HTTP 協議解析
  • 高性能服務器的關鍵在于 Reactor 模型 的合理應用。
  • 最終實現是一個 主從 Reactor 高性能服務器框架,并將功能拆分為模塊化組件,便于擴展和復用。

二、功能模塊劃分

為了實現一個帶有協議支持的 Reactor 高性能服務器,項目分為兩大模塊:

  • SERVER 模塊:實現 Reactor 模型的 TCP 服務器。
  • 協議模塊:為服務器提供應用層協議支持。

2.1、SERVER 模塊

負責對連接和線程進行管理,分為三個方向:

  • 監聽連接管理
  • 通信連接管理
  • 超時連接管理

子模塊

Buffer 模塊
  • 通信緩沖區,提供用戶態接收和發送緩沖區。
Socket 模塊
  • 封裝套接字操作。
Channel 模塊
  • 管理描述符的 I/O 事件(讀、寫、錯誤等)。
  • 與 Poller 配合,觸發事件時回調相應處理函數。
Connection 模塊
  • 封裝 Buffer、Socket、Channel,管理一個通信套接字。
  • 每個新連接由一個 Connection 管理。
  • 提供:
    • 回調函數(連接建立、事件、新數據、關閉)。
    • 接口(數據發送、連接關閉)。
    • 用戶態緩沖區(接收 + 發送)。
  • 處理流程
    1. 注冊 Channel 回調并加入 Poller 監控。
    2. IO 可讀 → 讀數據到用戶緩沖區 → 調用業務回調。
    3. 業務處理完 → 寫數據到發送緩沖區。
    4. Poller 通知可寫 → 調用寫回調 → 數據發送到內核。
Acceptor 模塊
  • 管理監聽套接字。
  • 負責獲取新連接,為其創建 Connection 對象。
TimerQueue 模塊
  • 定時任務管理器。
  • 管理 Connection 生命周期,釋放超時連接。
  • 基于 timerfd + Channel 實現。
Poller 模塊
  • 封裝 epoll,管理 IO 事件(添加、修改、刪除、獲取活躍事件)。
EventLoop 模塊
  • 核心 Reactor 單元,一個線程一個 EventLoop。
  • 管理 Poller、TimerQueue、任務隊列。
  • 保證所有 Connection 操作都在其綁定線程內完成。
  • 機制
    • eventfd 用于任務隊列喚醒 epoll 阻塞。
    • 處理順序:Poller 就緒事件 → Channel 回調 → 任務隊列執行。
TcpServer 模塊
  • 封裝整個 TCP 服務器:
    • BaseLoop(主 Reactor)。
    • EventLoopThreadPool(子 Reactor 池)。
    • Acceptor(監聽套接字)。
    • Hash 表(管理所有 Connection)。
  • 處理流程
    1. 實例化時初始化 BaseLoop、Acceptor、線程池和連接表。
    2. Acceptor 接收新連接 → 創建 Connection → 設置回調 → 加入哈希表 → 分配 EventLoop → 設置定時銷毀任務 → 加入 Poller。
    3. 啟動 BaseLoop。

模塊關系圖:

在這里插入圖片描述

2.2、HTTP 協議模塊

HTTP 協議模塊用于為高并發服務器提供協議支持,簡化 HTTP 服務器的搭建過程。
它由多個子模塊組成,每個模塊負責不同的功能。


模塊劃分

Util 模塊(工具模塊)
  • 作用:提供常用的工具函數,避免重復造輪子。
  • 功能示例
    • URL 編解碼(encode/decode)
    • 文件讀寫操作(讀取靜態資源文件)
    • 字符串處理(分割、去空格、大小寫轉換等)

?? 可以認為是 基礎支撐庫


HttpRequest 模塊(HTTP 請求數據模塊)
  • 作用:負責存儲和管理 解析后的 HTTP 請求信息
  • 核心字段
    • 請求行(method、URL、version)
    • 請求頭部(headers)
    • 請求正文(body,可能是 JSON/表單數據)
  • 職責
    • 解析原始請求字符串
    • 提供統一接口讓上層業務獲取請求內容

?? 相當于 HTTP 請求的 數據結構


HttpResponse 模塊(HTTP 響應數據模塊)
  • 作用:負責生成和管理 HTTP 響應數據
  • 核心字段
    • 狀態行(version、status code、reason phrase)
    • 響應頭部(headers)
    • 響應正文(body,HTML/JSON/文件內容)
  • 職責
    • 設置響應碼、響應頭、響應體
    • 將結構化數據拼裝成完整的 HTTP 響應字符串

?? 相當于 HTTP 響應的 數據結構 + 序列化器


HttpContext 模塊(HTTP 上下文模塊)
  • 作用:解決 請求接收的不完整性 問題。
  • 問題場景
    • TCP 是流式協議,一次 recv 可能拿到的是半個請求,或者多個請求拼在一起。
  • 職責
    • 緩存未完整的請求數據
    • 持續解析,直到完整請求被解析成 HttpRequest
    • 管理請求解析狀態(正在解析頭部/正在解析 body/解析完成)

?? 可以理解為 請求的粘包拆包處理器


HttpServer 模塊(HTTP 服務器模塊)
  • 作用:對外暴露簡單接口,開發者只需要關注“注冊路由 + 處理邏輯”。
  • 內部結構
    • TcpServer 對象:負責底層 TCP 連接、收發數據。
    • 兩個接口(供 TcpServer 回調):
      • 連接建立成功 → 設置 HttpContext
      • 數據到來 → 調用解析邏輯并觸發業務回調
    • 請求-處理函數映射表(hash-map)
      • key: 路徑(URL)或方法+路徑組合
      • value: 業務處理函數
  • 職責
    • 接收請求
    • 匹配路由
    • 調用對應的業務處理函數
    • 將業務結果封裝成 HttpResponse 并返回

?? 開發者只需要寫業務邏輯,比如:

server.Get("/hello", [](const HttpRequest& req, HttpResponse* resp){resp->SetBody("Hello World!");resp->SetStatus(200);
});

三、C++11 技術點與功能用例


3.1 bind 概念

  • std::bind 是通用的函數適配器。
  • 接受函數對象和參數,返回一個新的函數對象。
  • 新函數對象的參數可以:
    • 已經綁定(固定值)
    • 或使用占位符 std::placeholders::_1, _2... 預留,調用時傳入。

函數原型

template <class Fn, class... Args>
bind(Fn&& fn, Args&&... args);

示例 1:綁定固定參數或預留參數

#include <iostream>
#include <functional>
#include <unistd.h>class Test {
public:Test() { std::cout << "構造" << std::endl; }~Test() { std::cout << "析構" << std::endl; }
};void del(const Test *t, int num) {std::cout << num << std::endl;delete t;
}int main() {Test *t = new Test;// 第1個參數固定為 t,第2個參數預留std::function<void(int)> cb = std::bind(del, t, std::placeholders::_1);cb(10); // 調用綁定函數while(1) sleep(1);return 0;
}

輸出:

構造
10
析構

示例 2:任務池中的應用

#include <iostream>
#include <string>
#include <vector>
#include <functional>void print(const std::string &str) {std::cout << str << std::endl;
}int main() {using Functor = std::function<void()>;std::vector<Functor> task_pool;task_pool.push_back(std::bind(print, "我是"));task_pool.push_back(std::bind(print, "大"));task_pool.push_back(std::bind(print, "帥哥"));for (auto &functor : task_pool) {functor();}return 0;
}

輸出:

我是
大
帥哥

總結

  • std::bind 用于生成可調用對象并綁定參數。

  • 占位符 _1, _2… 用于預留參數。

  • 在任務池/線程池中,可用 bind 將函數任務封裝,降低耦合度。

這種格式特點:

  • 標題層次分明(概念 → 示例 → 總結)
  • 示例代碼獨立清晰
  • 輸出結果緊跟代碼塊
  • 便于快速查閱和復制

3.2 Linux 定時器 timerfd

創建定時器

Linux 提供timerfd_create 創建定時器:

#include <sys/timerfd.h>int timerfd_create(int clockid, int flags);
  • clockid

    • CLOCK_REALTIME:系統實時時間,修改系統時間會影響定時器。

    • CLOCK_MONOTONIC:從開機到現在的相對時間,不受系統時間修改影響。

  • flags:通常為 0(阻塞模式)。


設置定時器時間

int timerfd_settime(int fd, int flags, struct itimerspec *new_value, struct itimerspec *old_value);
  • fd:timerfd_create 返回的文件描述符

  • flags:0 表示相對時間,1 表示絕對時間

  • struct itimerspec

struct timespec {time_t tv_sec;   // 秒long tv_nsec;    // 納秒
};struct itimerspec {struct timespec it_interval; // 第一次之后的超時間隔struct timespec it_value;    // 第一次超時時間
};

當定時器超時時,會在 fd 寫入 8 字節整數,表示自上次讀取后超時次數。


示例:每隔 3 秒觸發一次

#include <iostream>
#include <unistd.h>
#include <sys/timerfd.h>int main() {int timerfd = timerfd_create(CLOCK_MONOTONIC, 0);struct itimerspec itm;itm.it_value.tv_sec = 3;itm.it_value.tv_nsec = 0;itm.it_interval.tv_sec = 3;itm.it_interval.tv_nsec = 0;timerfd_settime(timerfd, 0, &itm, NULL);time_t start = time(NULL);while (1) {uint64_t tmp;int ret = read(timerfd, &tmp, sizeof(tmp));if (ret < 0) return -1;std::cout << tmp << " " << time(NULL) - start << std::endl;}close(timerfd);return 0;
}

輸出:

1 3
1 6
1 9
1 12

3.3 時間輪思想

問題

遍歷所有連接判斷超時,效率低。
解決方法:

  • 小根堆

  • 時間輪


原理

  • 類似鐘表:

    • 數組表示時間槽,每秒 tick 向后走一步。

    • 將任務放入 (tick + delay) 的槽位。

  • 同一時間槽可以有多個任務。

  • 支持多層時間輪(秒輪、分輪、時輪),但 30 秒以內的定時任務通常只需單層時間輪。


延遲任務 + 智能指針

  • 利用 shared_ptr 管理任務:

    • 刷新任務時增加計數,舊任務失效。

    • 計數為 0 時真正析構,執行定時任務。

在這里插入圖片描述


定時任務類設計

using OnTimerCallback = std::function<void()>;
using ReleaseCallback = std::function<void()>;class Timer {
private:int _timeout;bool _canceled = false;uint64_t _timer_id;OnTimerCallback _timer_callback;ReleaseCallback _release_callback;public:Timer(uint64_t timer_id, int timeout): _timer_id(timer_id), _timeout(timeout) {}~Timer() {if (_release_callback) _release_callback();if (_timer_callback && !_canceled) _timer_callback();}int delay_time() { return _timeout; }void canceled() { _canceled = true; }void set_on_time_callback(const OnTimerCallback &cb) { _timer_callback = cb; }void set_release_callback(const ReleaseCallback &cb) { _release_callback = cb; }
};

時間輪實現

#define MAX_TIMEOUT 60class TimerQueue {
private:using PtrTimer = std::shared_ptr<Timer>;using WeakTimer = std::weak_ptr<Timer>;using Bucket = std::vector<PtrTimer>;using BucketList = std::vector<Bucket>;int _tick = 0;int _capacity = MAX_TIMEOUT;BucketList _conns;std::unordered_map<uint64_t, WeakTimer> _timers;public:TimerQueue(): _tick(0), _capacity(MAX_TIMEOUT), _conns(_capacity) {}bool has_timer(uint64_t id) {return _timers.find(id) != _timers.end();}void timer_add(const OnTimerCallback &cb, int delay, uint64_t id) {if (delay <= 0 || delay > _capacity) return;PtrTimer timer(new Timer(id, delay));timer->set_on_time_callback(cb);timer->set_release_callback(std::bind(&TimerQueue::remove_weaktimer_from_timerqueue, this, id));_timers[id] = WeakTimer(timer);_conns[(_tick + delay) % _capacity].push_back(timer);}void timer_refresh(uint64_t id) {auto it = _timers.find(id);assert(it != _timers.end());int delay = it->second.lock()->delay_time();_conns[(_tick + delay) % _capacity].push_back(it->second.lock());}void timer_cancel(uint64_t id) {auto it = _timers.find(id);assert(it != _timers.end());if (auto pt = it->second.lock()) pt->canceled();}void remove_weaktimer_from_timerqueue(uint64_t id) {_timers.erase(id);}void run_ontime_task() {_tick = (_tick + 1) % _capacity;_conns[_tick].clear();}
};

使用示例

class TimerTest {
private:int _data;
public:TimerTest(int data): _data(data) { std::cout << "test 構造!\n"; }~TimerTest() { std::cout << "test 析構!\n"; }
};void del(TimerTest *t) { delete t; }int main() {TimerQueue tq;TimerTest *t = new TimerTest(10);int id = 3;tq.timer_add(std::bind(del, t), 5, id);// 刷新定時任務for (int i = 0; i < 5; i++) {sleep(1);tq.timer_refresh(id);std::cout << "刷新了1下定時任務!\n";tq.run_ontime_task();}std::cout << "刷新停止, 5s后釋放任務將被執行\n";while (1) {sleep(1);tq.run_ontime_task();if (!tq.has_timer(id)) {std::cout << "定時任務已執行完畢!\n";break;}}return 0;
}

輸出:

test 構造!
刷新了1下定時任務!
刷新了1下定時任務!
刷新了1下定時任務!
刷新了1下定時任務!
刷新了1下定時任務!
刷新停止, 5s后釋放任務將被執行
test 析構!

總結

  • 利用 timerfd 可以實現秒級定時器。

  • 單層時間輪高效管理大量定時任務。

  • 利用 shared_ptr 延遲任務析構,實現刷新任務邏輯。

  • 適合高并發服務器中處理連接超時問題。


3.4 C++ 正則庫()的簡單使用

正則表達式(Regular Expression)是一種描述字符串匹配模式的工具。它可以用來:

  • 檢查字符串是否包含某種子串

  • 替換匹配的子串

  • 提取符合條件的子串

在 HTTP 請求解析中,正則表達式可以讓程序邏輯更簡潔靈活。不過需要注意,正則表達式通常比直接字符串處理效率低。


HTTP 請求首行解析

下面示例演示如何使用正則表達式解析 HTTP 請求首行:

#include <iostream>
#include <string>
#include <regex>void req_line() {std::cout << "------------------first line start-----------------\n";std::string str = "GET /hello?a=b&c=d HTTP/1.1\r\n";std::regex re("(GET|HEAD|POST|PUT|DELETE) (([^?]+)(?:\\?(.*?))?) (HTTP/1\\.[01])(?:\r\n|\n)");std::smatch matches;std::regex_match(str, matches, re);for (int i = 0; i < matches.size(); ++i) {std::cout << i << ": " << matches[i] << std::endl;}if (matches[4].length() > 0) {std::cout << "have param!\n";} else {std::cout << "have not param!\n";}std::cout << "------------------first line end-----------------\n";
}int main() {req_line();return 0;
}

輸出示例:

------------------first line start-----------------
0: GET /hello?a=b&c=d HTTP/1.1
1: GET
2: /hello?a=b&c=d
3: /hello
4: a=b&c=d
5: HTTP/1.1
have param!
------------------first line end-----------------matches 的存儲說明:
matches[0]:整體首行
matches[1]:請求方法
matches[2]:整體 URL
matches[3]:路徑(? 之前)
matches[4]:查詢字符串
matches[5]:HTTP 協議版本

提取請求方法

void method_match(const std::string str) {std::cout << "------------------method start-----------------\n";std::regex re("(GET|HEAD|POST|PUT|DELETE) .*");std::smatch matches;std::regex_match(str, matches, re);std::cout << matches[0] << std::endl; // 整行std::cout << matches[1] << std::endl; // 方法std::cout << "------------------method over------------------\n";
}

提取請求路徑

void path_match(const std::string str) {std::cout << "------------------path start------------------\n";std::regex re("([^?]+).*");  // 匹配 ? 前的路徑std::smatch matches;std::regex_match(str, matches, re);std::cout << matches[0] << std::endl;std::cout << matches[1] << std::endl;std::cout << "------------------path over------------------\n";
}

提取查詢字符串

void query_match(const std::string str) {std::cout << "------------------query start------------------\n";std::regex re("(?:\\?(.*?))? .*"); std::smatch matches;std::regex_match(str, matches, re);std::cout << matches[0] << std::endl;std::cout << matches[1] << std::endl;std::cout << "------------------query over------------------\n";
}
(\\?(.*?))? 表示匹配以 ? 開頭的查詢字符串(可能沒有)。

提取協議版本

void version_match(const std::string str) {std::cout << "------------------version start------------------\n";std::regex re("(HTTP/1\\.[01])(?:\r\n|\n)");std::smatch matches;std::regex_match(str, matches, re);std::cout << matches[0] << std::endl;std::cout << matches[1] << std::endl;std::cout << "------------------version over------------------\n";
}

完整示例

int main() {req_line();method_match("GET /s");path_match("/search?name=bitejiuyeke ");query_match("?name=xiaoming&age=19 HTTP/1.1");version_match("HTTP/1.1\r\n");return 0;
}

輸出示例:

------------------first line start-----------------
0: GET /bitejiuyeke?a=b&c=d HTTP/1.1
1: GET
2: /bitejiuyeke?a=b&c=d
3: /bitejiuyeke
4: a=b&c=d
5: HTTP/1.1
have param!
------------------first line end-----------------
------------------method start-----------------
GET /s
GET
------------------method over------------------
------------------path start------------------
/search?name=bitejiuyeke 
/search
------------------path over------------------
------------------query start------------------
?name=xiaoming&age=19 HTTP/1.1
name=xiaoming&age=19
------------------query over------------------
------------------version start------------------
HTTP/1.1
HTTP/1.1
------------------version over------------------

總結

  1. 可以方便地解析 HTTP 請求首行、路徑、查詢字符串和版本號。

  2. 捕獲組 () 可以獲取匹配的子字符串。

  3. 非捕獲組 (?:…) 用于匹配但不捕獲。

  4. 懶惰匹配 *? 可以確保只匹配第一次出現的內容。

  5. 正則表達式雖然靈活,但性能通常低于直接字符串操作。


3.5 C++ 通用類型 Any 的實現與使用

在網絡編程中,每個 Connection 對象都需要管理協議處理的上下文。為了降低耦合度,上下文不能依賴具體協議,需要一個通用類型來存儲任意數據結構。

在 C 語言中,可以使用 void*,但在 C++ 中,我們可以使用 C++17 提供的 std::any,或者自己實現一個簡單的 Any 類型。


Any 類型的簡單實現

原理:

  1. 定義一個基類 placeholder,提供虛函數 type() 和 clone()。

  2. 定義模板子類 holder 保存實際類型的數據。

  3. Any 類持有 placeholder*,在運行時管理不同類型的數據。

#include <iostream>
#include <string>
#include <cassert>
#include <typeinfo>class Any {
public:Any() : _content(nullptr) {}template<typename T>Any(const T &val) : _content(new holder<T>(val)) {}Any(const Any &other) : _content(other._content ? other._content->clone() : nullptr) {}~Any() { if (_content) delete _content; }const std::type_info &type() { return _content ? _content->type() : typeid(void); }template<typename T>T* get() {assert(typeid(T) == _content->type());return &((holder<T>*)_content)->val;}template<typename T>Any& operator=(const T &val) {Any(val).swap(*this);return *this;}Any& operator=(Any other) {other.swap(*this);return *this;}private:class placeholder {public:virtual ~placeholder() {}virtual const std::type_info &type() = 0;virtual placeholder *clone() = 0;};template <typename T>class holder : public placeholder {public:holder(const T &v) : val(v) {}const std::type_info &type() { return typeid(T); }placeholder *clone() { return new holder(val); }T val;};void swap(Any &other) { std::swap(_content, other._content); }placeholder *_content;
};

測試Any類型

class Test {
public:std::string _data;Test(const std::string &data) : _data(data) { std::cout << "構造" << _data << std::endl; }Test(const Test &other) { _data = other._data; std::cout << "拷貝" << _data << std::endl; }~Test() { std::cout << "析構" << _data << std::endl; }
};int main() {// 基本類型Any any_a = 10;Any any_b = 20.5f;Any any_c = std::string("Hello World");std::cout << *any_a.get<int>() << std::endl;std::cout << *any_b.get<float>() << std::endl;std::cout << *any_c.get<std::string>() << std::endl;// 對象類型Test d("Leihou");Any any_d = d;Any any_e(d);Any any_f(any_d);Any any_g = any_d;// 不同類型的賦值Any any_h;any_h = 33;std::cout << *any_h.get<int>() << std::endl;any_h = std::string("Hello Any");std::cout << *any_h.get<std::string>() << std::endl;any_h = Any(Test("test"));std::cout << any_h.get<Test>()->_data << std::endl;return 0;
}

輸出示例:

10
20.5
Hello World
構造Leihou
拷貝Leihou
拷貝Leihou
拷貝Leihou
拷貝Leihou
析構Leihou
析構Leihou
析構Leihou
析構Leihou
析構Leihou
33
Hello Any
構造test
拷貝test
析構test
test
析構test

C++17 中的 std::any 使用

C++17 提供了標準的 std::any,使用起來更簡潔,不需要手動實現:

#include <iostream>
#include <string>
#include <any>class Test {
public:std::string _data;Test(const std::string &data) : _data(data) { std::cout << "構造" << _data << std::endl; }Test(const Test &other) { _data = other._data; std::cout << "拷貝" << _data << std::endl; }~Test() { std::cout << "析構" << _data << std::endl; }
};int main() {std::any a = 10;std::any b = 88.88;std::any c = std::string("bitejiuyeke");std::cout << *std::any_cast<int>(&a) << std::endl;std::cout << *std::any_cast<double>(&b) << std::endl;std::cout << *std::any_cast<std::string>(&c) << std::endl;Test d("Leihou");std::any any_d = d;std::any any_f;any_f = 33;std::cout << *std::any_cast<int>(&any_f) << std::endl;std::string s = "Hello World";any_f = s;std::cout << *std::any_cast<std::string>(&any_f) << std::endl;any_f = std::any(Test("test"));std::cout << std::any_cast<Test>(&any_f)->_data << std::endl;return 0;
}
注意:使用 std::any 需要 C++17 支持,推薦 g++ 7.3 及以上版本。

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

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

相關文章

浴室柜市占率第一,九牧重構數智衛浴新生態

作者 | 曾響鈴文 | 響鈴說2025年上半年&#xff0c;家居市場在政策的推動下展現出獨特的發展態勢。國家出臺的一系列鼓勵家居消費的政策&#xff0c;如“以舊換新”國補政策帶動超6000萬件廚衛產品煥新&#xff0c;以及我國超2.7億套房齡超20年的住宅進入改造周期&#xff0c;都…

源碼分析之Leaflet中TileLayer

概述 TileLayer 是 Layer 的子類&#xff0c;繼承自GridLayer基類&#xff0c;用于加載和顯示瓦片地圖。它提供了加載和顯示瓦片地圖的功能&#xff0c;支持自定義瓦片的 URL 格式和參數。 源碼分析 源碼實現 TileLayer的源碼實現如下&#xff1a; export var TileLayer GridL…

php學習(第二天)

一.網站基本概念-服務器 1.什么是服務器? 1.1定義 服務器&#xff08;server&#xff09;,也稱伺服器&#xff0c;是提供計算服務的設備。 供計算服務的設備” 這里的“設備”不僅指物理機器&#xff08;如一臺配有 CPU、內存、硬盤的計算機&#xff09;&#xff0c;也可以指…

C++(友元和運算符重載)

目錄 友元&#xff1a; 友元函數&#xff1a; 示例&#xff1a; 友元類&#xff1a; 示例&#xff1a; 優點&#xff1a; 注意事項&#xff1a; 運算符重載&#xff1a; 注意&#xff1a; 示例&#xff1a; 友元&#xff1a; C中如果想要外部函數或者類對一個類的pr…

和平精英風格射擊游戲開發指南

本教程將完整講解如何開發一款和平精英風格的HTML射擊游戲&#xff0c;涵蓋核心設計理念、代碼架構與關鍵實現細節。 核心設計架構 游戲機制系統 角色控制系統&#xff1a;通過鍵盤實現玩家移動戰斗系統&#xff1a;子彈發射與碰撞檢測道具系統&#xff1a;武器、彈藥和醫療包收…

21.1 《24GB顯存搞定LLaMA2-7B指令微調:QLoRA+Flash Attention2.0全流程實戰》

24GB顯存搞定LLaMA2-7B指令微調:QLoRA+Flash Attention2.0全流程實戰 實戰 LLaMA2-7B 指令微調 一、指令微調技術背景 指令微調(Instruction Tuning)是大模型訓練中的關鍵技術突破點。與傳統全量微調(Full Fine-Tuning)相比,指令微調通過特定格式的指令-響應數據訓練,…

周志華《機器學習導論》第10章 降維與度量學習

https://www.lamda.nju.edu.cn/aml24fall/slides/Chap10.pptx 目錄 1.MDS (Multiple Dimensional Scaling) 多維縮放方法 2. 主成分分析 (Principal Component Analysis, PCA) 2.1 凸優化證明 2.2 人臉識別降維應用 3. 核化PCA 4. 流行學習 4.1 LLE 局部線性嵌入&#…

Kubernetes 彈性伸縮:深入講解 HPA 和 VPA

1. 介紹 Kubernetes 提供了多種資源管理方式&#xff0c;其中 彈性伸縮&#xff08;Auto-scaling&#xff09;是最重要的特性之一。彈性伸縮可以根據應用的負載變化自動調整 Pod 的數量和資源&#xff0c;以確保在高負載下應用能夠正常運行&#xff0c;而在低負載時節省資源。在…

大數據畢業設計選題推薦-基于大數據的家庭能源消耗數據分析與可視化系統-Hadoop-Spark-數據可視化-BigData

?作者主頁&#xff1a;IT畢設夢工廠? 個人簡介&#xff1a;曾從事計算機專業培訓教學&#xff0c;擅長Java、Python、PHP、.NET、Node.js、GO、微信小程序、安卓Android等項目實戰。接項目定制開發、代碼講解、答辯教學、文檔編寫、降重等。 ?文末獲取源碼? 精彩專欄推薦?…

【Spring】原理解析:Spring Boot 自動配置的核心機制與實戰剖析

一、引言在當今的 Java 開發領域&#xff0c;Spring Boot 憑借其快速搭建項目、簡化配置等優勢&#xff0c;成為了眾多開發者的首選框架。而 Spring Boot 自動配置作為其核心特性之一&#xff0c;極大地提升了開發效率&#xff0c;讓開發者能夠更專注于業務邏輯的實現。本文將深…

Java forEach中不能用i++的原因以及代替方案

因為在 Lambda 表達式內部訪問的外部局部變量必須是 final 或 effectively final&#xff08;事實最終變量&#xff09;&#xff0c;而 i 操作試圖改變這個變量的值&#xff0c;違反了這一規定。下面我們來詳細拆解這個問題&#xff0c;讓你徹底明白。1. 一個具體的例子我們先看…

第十四屆藍橋杯青少組C++選拔賽[2023.1.15]第二部分編程題(2 、尋寶石)

參考程序&#xff1a;#include <bits/stdc.h> using namespace std;int main() {int N;cin >> N; // 讀入盒子數vector<int> a(N);for (int i 0; i < N; i) cin >> a[i]; // 讀入每個盒子的寶石數// N > 3&#xff08;題目保證&#x…

9120 部 TMDb 高分電影數據集 | 7 列全維度指標 (評分 / 熱度 / 劇情)+API 權威源 | 電影趨勢分析 / 推薦系統 / NLP 建模用

一、引言在影視行業分析與數據科學實踐中&#xff0c;高分電影數據的深度挖掘已成為平臺優化內容推薦、制片方研判市場趨勢、影迷發現優質作品的核心支撐 —— 通過上映年份與評分的關聯可捕捉電影質量演變、依托熱度與投票數能定位爆款潛質、結合劇情概述可開展情感與主題分析…

Tomcat PUT方法任意寫文件漏洞學習

1 PUT請求 PUT請求是一種在HTTP協議中常見的請求方法 1.1 基本原理 PUT請求是一種用于向指定資源位置上傳新的實體數據的請求方法&#xff0c;與其他請求方法的區別在于&#xff0c;PUT請求用于創建或者更新只當資源位置的實體數據。它與GET請求不同&#xff0c;PUT請求會替換掉…

【C++基礎】初識模板——一起步入泛型編程的大門

引言在 C 世界里&#xff0c;模板&#xff08;Template&#xff09;就像一把萬能鑰匙。它允許你編寫通用的代碼&#xff0c;讓編譯器在需要的時候為具體類型生成對應的函數或類。換句話說&#xff0c;模板是 C 泛型編程&#xff08;Generic Programming&#xff09; 的基石。 如…

項目管理框架如何影響團隊協作

在項目執行過程中&#xff0c;項目管理框架不僅是一套工具和流程&#xff0c;更是團隊協作方式的基礎。不同的項目管理框架會深刻影響團隊溝通效率、任務分配、決策方式和整體協同效果。 傳統框架通常強調層級與計劃&#xff0c;帶來高度規范化的協作&#xff1b;敏捷框架則強調…

正向代理,反向代理,負載均衡還有nginx

這是一個非常核心且重要的后端/運維知識領域。我會用盡可能清晰易懂的方式&#xff0c;結合生動的比喻&#xff0c;為你詳細梳理這些概念。核心概念一覽我們先從一個宏觀的角度來理解它們之間的關系&#xff1a;代理&#xff08;Proxy&#xff09;&#xff1a; 一個中間人的角色…

WebSocket壓縮傳輸優化:機器視覺高清流在DCS中的低延遲方案

引言在現代工業自動化領域&#xff0c;分布式控制系統&#xff08;DCS&#xff09;正面臨著前所未有的數據挑戰。隨著機器視覺技術的廣泛應用&#xff0c;高清視頻流已成為監控產品質量、檢測設備異常和保障生產安全的重要手段。然而&#xff0c;將720P、1080P甚至4K分辨率的高…

《Linux常見命令》

ls 功能&#xff1a;列出目錄下的子目錄與文件&#xff0c;對于文件&#xff0c;還會列出文件名及其他信息。 語法&#xff1a;ls [選項] [目錄或文件] 1.常用選項及說明選項說明-a列出目錄下的所有文件&#xff0c;包括以 . 開頭的隱含文件-d將目錄象文件一樣顯示&#xff0c;…

Python數據分析:函數定義時的位置參數。

目錄1 代碼示例2 歡迎糾錯3 免費爬蟲4 論文寫作/Python 學習智能體1 代碼示例 直接上代碼。 def pargs1(a, b):"""先看確定數量的位置參數。最簡單的位置參數。a和b都叫而且只能叫“位置參數”。所謂確定數量&#xff0c;很明顯&#xff0c;是兩個就是兩個&…