自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

硬核實(shí)戰(zhàn):回調(diào)函數(shù)到底是個(gè)啥?一文帶你從原理到實(shí)戰(zhàn)徹底掌握C/C++回調(diào)函數(shù)

開發(fā)
作為一個(gè)在生產(chǎn)環(huán)境中與回調(diào)函數(shù)打了多年交道的開發(fā)者,今天我想分享一些真正實(shí)用的經(jīng)驗(yàn),帶你揭開回調(diào)函數(shù)的神秘面紗,從理論到實(shí)戰(zhàn)全方位掌握這個(gè)強(qiáng)大而常見的編程技巧。

網(wǎng)上講回調(diào)函數(shù)的文章不少,但大多淺嘗輒止、缺少系統(tǒng)性,更別提實(shí)戰(zhàn)場(chǎng)景和踩坑指南了。作為一個(gè)在生產(chǎn)環(huán)境中與回調(diào)函數(shù)打了多年交道的開發(fā)者,今天我想分享一些真正實(shí)用的經(jīng)驗(yàn),帶你揭開回調(diào)函數(shù)的神秘面紗,從理論到實(shí)戰(zhàn)全方位掌握這個(gè)強(qiáng)大而常見的編程技巧。

開篇:那些年,我們被回調(diào)函數(shù)整懵的日子

還記得我剛開始學(xué)編程時(shí),遇到"回調(diào)函數(shù)"這個(gè)詞簡(jiǎn)直一臉懵:

  • "回調(diào)?是不是打電話回去的意思?"
  • "函數(shù)還能回過(guò)頭調(diào)用?這是什么黑魔法?"
  • "為啥代碼里有個(gè)函數(shù)指針傳來(lái)傳去的?這是在干啥?"

如果你也有這些疑問(wèn),那恭喜你,今天這篇文章就是為你量身定做的!

一、什么是回調(diào)函數(shù)?先來(lái)個(gè)通俗解釋

回調(diào)函數(shù)本質(zhì)上就是:把一個(gè)函數(shù)當(dāng)作參數(shù)傳給另一個(gè)函數(shù),在合適的時(shí)機(jī)再被"回頭調(diào)用"。

這么說(shuō)太抽象?那我們來(lái)個(gè)生活中的例子:

想象你去火鍋店吃飯,但發(fā)現(xiàn)需要排隊(duì)。有兩種方式等位:

  • 傻等法:站在門口一直盯著前臺(tái),不停問(wèn)"到我了嗎?到我了嗎?"
  • 回調(diào)法:拿個(gè)小 buzzer(呼叫器),該干嘛干嘛去,等輪到你時(shí),buzzer 會(huì)自動(dòng)震動(dòng)提醒你

顯然第二種方式更高效!這就是回調(diào)的思想:

  • 小buzzer就是你傳遞的"回調(diào)函數(shù)"
  • 餐廳前臺(tái)就是接收回調(diào)的函數(shù)
  • buzzer震動(dòng)就是回調(diào)函數(shù)被執(zhí)行
  • 你不用一直守著,解放了自己去做其他事

回調(diào)函數(shù)的核心思想是:"控制反轉(zhuǎn)"(IoC)—— 把"何時(shí)執(zhí)行"的控制權(quán)交給了別人,而不是自己一直輪詢檢查。

二、為什么需要回調(diào)函數(shù)?

在深入代碼前,我們先搞清楚為啥需要這玩意兒?回調(diào)函數(shù)解決了哪些問(wèn)題?

  • 解耦合:調(diào)用者不需要知道被調(diào)用者的具體實(shí)現(xiàn)
  • 異步處理:可以在事件發(fā)生時(shí)才執(zhí)行相應(yīng)代碼,不需要一直等待
  • 提高擴(kuò)展性:同一個(gè)函數(shù)可以接受不同的回調(diào)函數(shù),實(shí)現(xiàn)不同的功能
  • 實(shí)現(xiàn)事件驅(qū)動(dòng):GUI編程、網(wǎng)絡(luò)編程等領(lǐng)域的基礎(chǔ)

三、回調(diào)函數(shù)的基本結(jié)構(gòu):代碼詳解

好了,說(shuō)了這么多,來(lái)看看 C/C++ 中回調(diào)函數(shù)到底長(zhǎng)啥樣:

// 1. 定義回調(diào)函數(shù)類型(函數(shù)指針類型)
typedef void (*CallbackFunc)(int);

// 2. 實(shí)際的回調(diào)函數(shù)
void onTaskCompleted(int result) {
    printf("哇!任務(wù)完成了!結(jié)果是: %d\n", result);
}

// 3. 接收回調(diào)函數(shù)的函數(shù)
void doSomethingAsync(CallbackFunc callback) {
    printf("開始執(zhí)行任務(wù)...\n");
    // 假設(shè)這里是一些耗時(shí)操作
    int result = 42;
    printf("任務(wù)執(zhí)行完畢,準(zhǔn)備調(diào)用回調(diào)函數(shù)...\n");
    // 操作完成,調(diào)用回調(diào)函數(shù)
    callback(result);
}

// 4. 主函數(shù)
int main() {
    // 把回調(diào)函數(shù)傳遞過(guò)去
    doSomethingAsync(onTaskCompleted);
    return0;
}

上面的代碼中:

  • CallbackFunc 是一個(gè)函數(shù)指針類型,它定義了回調(diào)函數(shù)的簽名
  • onTaskCompleted 是實(shí)際的回調(diào)函數(shù),它會(huì)在任務(wù)完成時(shí)被調(diào)用
  • doSomethingAsync 是接收回調(diào)函數(shù)的函數(shù),它在完成任務(wù)后會(huì)調(diào)用傳入的回調(diào)函數(shù)
  • 在 main 函數(shù)中,我們將 onTaskCompleted 作為參數(shù)傳給了 doSomethingAsync

注意函數(shù)指針的定義:typedef void (*CallbackFunc)(int);

  • void 表示回調(diào)函數(shù)不返回值
  • (*CallbackFunc) 表示這是一個(gè)函數(shù)指針類型,名為 CallbackFunc
  • (int) 表示這個(gè)函數(shù)接收一個(gè) int 類型的參數(shù)

這就是回調(diào)函數(shù)的基本結(jié)構(gòu)!核心就是把函數(shù)的地址當(dāng)作參數(shù)傳遞,然后在合適的時(shí)機(jī)調(diào)用它。

四、回調(diào)函數(shù)的本質(zhì):深入理解函數(shù)指針

要真正理解回調(diào)函數(shù),必須先搞清楚函數(shù)指針。在C/C++中,函數(shù)在內(nèi)存中也有地址,可以用指針指向它們。

// 普通函數(shù)
int add(int a, int b) {
    return a + b;
}

int main() {
    // 聲明一個(gè)函數(shù)指針
    int (*funcPtr)(int, int);
    
    // 讓指針指向add函數(shù)
    funcPtr = add;
    
    // 通過(guò)函數(shù)指針調(diào)用函數(shù)
    int result = funcPtr(5, 3);
    printf("結(jié)果是: %d\n", result);  // 輸出: 結(jié)果是: 8
    
    return0;
}

這里的 funcPtr 就是函數(shù)指針,它指向了 add 函數(shù)。我們可以通過(guò)這個(gè)指針調(diào)用函數(shù),就像通過(guò)普通指針訪問(wèn)變量一樣。

回調(diào)函數(shù)的本質(zhì)就是利用函數(shù)指針,實(shí)現(xiàn)了函數(shù)的"延遲調(diào)用"或"條件調(diào)用"。它讓一個(gè)函數(shù)可以在未來(lái)某個(gè)時(shí)刻,滿足某個(gè)條件時(shí),被另一個(gè)函數(shù)調(diào)用。

五、C與C++中的不同回調(diào)方式

C和C++提供了不同的實(shí)現(xiàn)回調(diào)的方式,讓我們比較一下:

1. C語(yǔ)言中的函數(shù)指針

這是最基礎(chǔ)的方式,就像我們前面看到的:

typedef void (*Callback)(int);

void someFunction(Callback cb) {
    // ...
    cb(42);
}

2. C++中的函數(shù)對(duì)象(Functor)

// 函數(shù)對(duì)象類
class PrintCallback {
public:
    void operator()(int value) {
        std::cout << "值是: " << value << std::endl;
    }
};

// 接收函數(shù)對(duì)象的函數(shù)
template<typename Func>
void doSomething(Func callback) {
    callback(100);
}

int main() {
    PrintCallback printer;
    doSomething(printer);  // 輸出: 值是: 100
    return0;
}

3. C++11中的 std::function 和 lambda 表達(dá)式

這是最現(xiàn)代的方式,也最靈活:

// 使用std::function
void doTask(std::function<void(int)> callback) {
    callback(200);
}

int main() {
    // 使用lambda表達(dá)式
    doTask([](int value) {
        std::cout << "Lambda被調(diào)用,值是: " << value << std::endl;
    });
    
    // 帶捕獲的lambda
    int factor = 10;
    doTask([factor](int value) {
        std::cout << "結(jié)果是: " << value * factor << std::endl;
    });
    
    return0;
}

C++11的std::function和 lambda 表達(dá)式讓回調(diào)變得更加靈活,特別是 lambda 可以捕獲外部變量,這在 C 語(yǔ)言中很難實(shí)現(xiàn)。

六、回調(diào)函數(shù)的實(shí)戰(zhàn)案例

光說(shuō)不練假把式,來(lái)幾個(gè)實(shí)際案例感受一下回調(diào)函數(shù)的強(qiáng)大:

案例1:自定義排序

假設(shè)我們有一個(gè)數(shù)組,想按照不同的規(guī)則排序:

// 定義比較函數(shù)類型
typedef int (*CompareFunc)(const void*, const void*);

// 升序比較
int ascendingCompare(const void* a, const void* b) {
    return (*(int*)a - *(int*)b);
}

// 降序比較
int descendingCompare(const void* a, const void* b) {
    return (*(int*)b - *(int*)a);
}

// 自定義排序函數(shù)
void customSort(int arr[], int size, CompareFunc compare) {
    qsort(arr, size, sizeof(int), compare);
}

int main() {
    int numbers[] = {-42, 8, -15, 16, -23, 4};
    int size = sizeof(numbers) / sizeof(numbers[0]);
    // 升序排序
    customSort(numbers, size, ascendingCompare);
    
    // 降序排序
    customSort(numbers, size, descendingCompare);
    
    return0;
}

這個(gè)例子展示了回調(diào)函數(shù)最常見的用途之一:通過(guò)傳入不同的比較函數(shù),實(shí)現(xiàn)不同的排序規(guī)則,而無(wú)需修改排序算法本身。

案例2:事件處理系統(tǒng)

GUI編程中,回調(diào)函數(shù)無(wú)處不在。下面我們模擬一個(gè)簡(jiǎn)單的事件系統(tǒng):

// 事件類型
enum EventType { CLICK, HOVER, KEY_PRESS };

// 事件結(jié)構(gòu)體
struct Event {
    EventType type;
    int x, y;
    char key;
};

// 定義回調(diào)函數(shù)類型
typedef void (*EventCallback)(const Event*);

// 各種事件處理函數(shù)
void onClickCallback(const Event* event) {
    printf("點(diǎn)擊事件觸發(fā)了!坐標(biāo): (%d, %d)\n", 
           event->x, event->y);
}

void onKeyPressCallback(const Event* event) {
    printf("按鍵事件觸發(fā)了!按下的鍵是: %c\n", 
           event->key);
}
...

// 事件處理器結(jié)構(gòu)體
struct EventHandler {
    EventCallback callbacks[10];  // 假設(shè)最多10種事件類型
};

// 注冊(cè)事件回調(diào)
void registerCallback(EventHandler* handler, EventType type, EventCallback callback) {
    handler->callbacks[type] = callback;
}

// 事件分發(fā)器
void dispatchEvent(EventHandler* handler, const Event* event) {
    if (handler->callbacks[event->type] != NULL) {
        handler->callbacks[event->type](event);
    }
}

int main() {
    // 創(chuàng)建并初始化事件處理器
    EventHandler handler;
    
    // 注冊(cè)回調(diào)函數(shù)
    registerCallback(&handler, CLICK, onClickCallback);
    
    // 模擬點(diǎn)擊事件
    Event clickEvent = {CLICK, 100, 200};
    dispatchEvent(&handler, &clickEvent);
    
    return0;
}

這個(gè)例子模擬了 GUI 程序中的事件處理機(jī)制:不同類型的事件發(fā)生時(shí),系統(tǒng)會(huì)調(diào)用相應(yīng)的回調(diào)函數(shù)。這是所有 GUI框架的基礎(chǔ)設(shè)計(jì)模式。

案例3:帶用戶數(shù)據(jù)的回調(diào)函數(shù)

在實(shí)際應(yīng)用中,我們經(jīng)常需要給回調(diào)函數(shù)傳遞額外的上下文數(shù)據(jù)。下面看看幾種實(shí)現(xiàn)方式:

使用 void 指針傳遞用戶數(shù)據(jù)(C語(yǔ)言風(fēng)格)

// 用戶數(shù)據(jù)結(jié)構(gòu)體
struct UserData {
    constchar* name;
    int id;
};

// 回調(diào)函數(shù)類型
typedef void (*Callback)(int result, void* userData);

// 實(shí)際的回調(diào)函數(shù)
void processResult(int result, void* userData) {
    UserData* data = (UserData*)userData;
    printf("用戶 %s (ID: %d) 收到結(jié)果: %d\n", 
        data->name, data->id, result);
}

// 執(zhí)行任務(wù)的函數(shù)
void executeTask(Callback callback, void* userData) {
    int result = 100;
    callback(result, userData);
}

int main() {
    // 創(chuàng)建用戶數(shù)據(jù)
    UserData user = {"張三", 1001};

    // 執(zhí)行任務(wù)
    executeTask(processResult, &user);

    return0;
}

這種方式通過(guò)void*類型參數(shù)傳遞任意類型的數(shù)據(jù),是C語(yǔ)言中最常見的方式。但缺點(diǎn)是缺乏類型安全性,容易出錯(cuò)。

使用C++11的 std::function 和 lambda 表達(dá)式

// 使用std::function定義回調(diào)類型
using TaskCallback = std::function<void(int)>;

// 執(zhí)行任務(wù)的函數(shù)
void executeTask(TaskCallback callback) {
    int result = 300;
    callback(result);
}

int main() {
    // 使用lambda捕獲局部變量
    std::string userName = "用戶1";
    int userId = 2001;

    // lambda捕獲外部變量
    executeTask([userName, userId](int result) {
        std::cout << userName << " (ID: " << userId 
            << ") 收到結(jié)果: " << result << std::endl;
    });

    return0;
}

這種方式最靈活,lambda表達(dá)式可以直接捕獲周圍環(huán)境中的變量,大大簡(jiǎn)化了代碼。

七、回調(diào)函數(shù)的設(shè)計(jì)模式

回調(diào)函數(shù)在各種設(shè)計(jì)模式中廣泛應(yīng)用,下面介紹兩個(gè)常見的模式:

1. 觀察者模式(Observer Pattern)

觀察者模式中,多個(gè)觀察者注冊(cè)到被觀察對(duì)象,當(dāng)被觀察對(duì)象狀態(tài)變化時(shí),通知所有觀察者:

// 使用C++11的方式實(shí)現(xiàn)觀察者模式
class Subject {
private:
    // 存儲(chǔ)觀察者的回調(diào)函數(shù)
    std::vector<std::function<void(conststd::string&)>> observers;
    
public:
    // 添加觀察者
    void addObserver(std::function<void(const std::string&)> observer) {
        observers.push_back(observer);
    }
    
    // 通知所有觀察者
    void notifyObservers(const std::string& message) {
        for (auto& observer : observers) {
            observer(message);
        }
    }
};

這個(gè)模式在GUI編程、消息系統(tǒng)、事件處理中非常常見。

2. 策略模式(Strategy Pattern)

策略模式使用回調(diào)函數(shù)實(shí)現(xiàn)不同的算法策略:

// 定義策略類型(使用回調(diào)函數(shù))
using SortStrategy = std::function<void(std::vector<int>&)>;

// 排序上下文類
class Sorter {
private:
    SortStrategy strategy;
    
public:
    Sorter(SortStrategy strategy) : strategy(strategy) {}
    
    void setStrategy(SortStrategy newStrategy) {
        strategy = newStrategy;
    }
    
    void sort(std::vector<int>& data) {
        strategy(data);
    }
};

策略模式允許在運(yùn)行時(shí)切換算法,非常靈活。

八、回調(diào)函數(shù)的陷阱與最佳實(shí)踐

使用回調(diào)函數(shù)雖然強(qiáng)大,但也存在一些潛在的問(wèn)題和陷阱。下面總結(jié)一些常見的坑和相應(yīng)的最佳實(shí)踐:

1. 生命周期問(wèn)題

陷阱:回調(diào)函數(shù)中引用了已經(jīng)被銷毀的對(duì)象。

void dangerousCallback() {
    char* buffer = new char[100];
    
    // 注冊(cè)一個(gè)在未來(lái)執(zhí)行的回調(diào)函數(shù)
    registerCallback([buffer]() {
        // 危險(xiǎn)!此時(shí)buffer可能已經(jīng)被刪除
        strcpy(buffer, "Hello");
    });
    
    // buffer在這里被刪除
    delete[] buffer;
}

最佳實(shí)踐:

  • 使用智能指針管理資源
void safeCallback() {
    // 使用智能指針
    auto buffer = std::make_shared<std::vector<char>>(100);
    
    // 智能指針會(huì)在所有引用消失時(shí)自動(dòng)釋放
    registerCallback([buffer]() {
        // 安全!即使原始作用域結(jié)束,buffer仍然有效
        std::copy_n("Hello", 6, buffer->data());
    });
}
  • 提供取消注冊(cè)機(jī)制
class CallbackManager {
    std::map<int, std::function<void()>> callbacks;
    int nextId = 0;
    
public:
    // 返回標(biāo)識(shí)符,用于取消注冊(cè)
    int registerCallback(std::function<void()> cb) {
        int id = nextId++;
        callbacks[id] = cb;
        return id;
    }
    
    void unregisterCallback(int id) {
        callbacks.erase(id);
    }
};

void safeUsage() {
    CallbackManager manager;
    
    // 保存ID用于取消注冊(cè)
    int callbackId = manager.registerCallback([]() { /* ... */ });
    
    // 在合適的時(shí)機(jī)取消注冊(cè)
    manager.unregisterCallback(callbackId);
}

2. 回調(diào)地獄(Callback Hell)

陷阱:嵌套太多層回調(diào),導(dǎo)致代碼難以理解和維護(hù)。

doTaskA([](int resultA) {
    doTaskB(resultA, [](int resultB) {
        doTaskC(resultB, [](int resultC) {
            // 代碼縮進(jìn)越來(lái)越深,難以閱讀和維護(hù)
        });
    });
});

最佳實(shí)踐:

  • 使用 std::async 和 std::future(C++11)
// C++11及以上
std::future<int> doTaskAAsync() {
    returnstd::async(std::launch::async, []() {
        return doTaskA();
    });
}

std::future<int> doTaskBAsync(int resultA) {
    returnstd::async(std::launch::async, [resultA]() {
        return doTaskB(resultA);
    });
}

std::future<int> doTaskCAsync(int resultB) {
    returnstd::async(std::launch::async, [resultB]() {
        return doTaskC(resultB);
    });
}

// 真正的異步鏈?zhǔn)秸{(diào)用
void chainedAsyncTasks() {
    try {
        // 啟動(dòng)任務(wù)A
        auto futureA = doTaskAAsync();
        
        // 等待A完成并啟動(dòng)B
        auto resultA = futureA.get();
        auto futureB = doTaskBAsync(resultA);
        
        // 等待B完成并啟動(dòng)C
        auto resultB = futureB.get();
        auto futureC = doTaskCAsync(resultB);
        
        // 獲取最終結(jié)果
        auto resultC = futureC.get();
        std::cout << "Final result: " << resultC << std::endl;
    }
    catch(conststd::exception& e) {
        std::cerr << "Error in task chain: " << e.what() << std::endl;
    }
}
  • 使用協(xié)程 (C++20)
// 使用C++20協(xié)程解決回調(diào)地獄
#include <coroutine>

// 偽代碼:簡(jiǎn)化的任務(wù)協(xié)程類型
template<typename T>
struct Task {
    struct promise_type {/* 協(xié)程必需的接口 */ };
    // 使用自動(dòng)生成的協(xié)程狀態(tài)機(jī)
};

// 異步任務(wù)A
Task<int> doTaskAAsync() {
    // co_return 返回值并結(jié)束協(xié)程 (類似return但用于協(xié)程)
    co_return doTaskA();
}

// 異步任務(wù)B - 接收A的結(jié)果作為輸入
Task<int> doTaskBAsync(int resultA) {
    co_return doTaskB(resultA);
}

// 異步任務(wù)C - 接收B的結(jié)果作為輸入
Task<int> doTaskCAsync(int resultB) {
    co_return doTaskC(resultB);
}

// 主任務(wù) - 協(xié)程方式鏈接所有任務(wù)
Task<int> processAllTasksAsync() {
    try {
        // co_await 暫停當(dāng)前協(xié)程,等待doTaskAAsync()完成
        // 協(xié)程暫停時(shí)不會(huì)阻塞線程,控制權(quán)返回給調(diào)用者
        int resultA = co_await doTaskAAsync();
        
        // 當(dāng)任務(wù)A完成后,協(xié)程從這里繼續(xù)執(zhí)行
        std::cout << "Task A completed: " << resultA << std::endl;
        
        // 等待任務(wù)B完成
        int resultB = co_await doTaskBAsync(resultA);
        std::cout << "Task B completed: " << resultB << std::endl;
        
        // 等待任務(wù)C完成
        int resultC = co_await doTaskCAsync(resultB);
        std::cout << "Task C completed: " << resultC << std::endl;
        
        // 返回最終結(jié)果
        co_return resultC;
    } 
    catch (conststd::exception& e) {
        std::cerr << "Error in coroutine chain: " << e.what() << std::endl;
        co_return-1;
    }
}

// 啟動(dòng)協(xié)程鏈 (偽代碼)
void runAsyncChain() {
    // 啟動(dòng)協(xié)程并等待完成
    auto task = processAllTasksAsync();
    int finalResult = syncAwait(task);  // 同步等待協(xié)程完成
    std::cout << "Final result: " << finalResult << std::endl;
}

3. 異常處理

陷阱:回調(diào)函數(shù)中拋出的異常無(wú)法被調(diào)用者捕獲。

void riskyCallback() {
    try {
        executeCallback([]() {
            throw std::runtime_error("回調(diào)中的錯(cuò)誤");  // 這個(gè)異常無(wú)法被外層捕獲
        });
    } catch (const std::exception& e) {
        // 這里捕獲不到回調(diào)中拋出的異常!
        std::cout << "捕獲到異常: " << e.what() << std::endl;
    }
}

最佳實(shí)踐:使用錯(cuò)誤碼代替異常

// 定義錯(cuò)誤碼
enumclass ErrorCode {
    Success = 0,
    GeneralError = -1,
    NetworkError = -2,
    TimeoutError = -3
    // 更多具體的錯(cuò)誤類型...
};

// 使用std::function
void executeSafe(std::function<void(int result, ErrorCode code, const std::string& message)> callback) {
    try {
        // 嘗試執(zhí)行操作
        int result = performOperation();
        callback(result, ErrorCode::Success, "操作成功");
    } catch (conststd::exception& e) {
        // 可以根據(jù)異常類型設(shè)置不同的錯(cuò)誤碼
        callback(0, ErrorCode::GeneralError, e.what());
    } catch (...) {
        callback(0, ErrorCode::GeneralError, "未知錯(cuò)誤");
    }
}

4. 線程安全問(wèn)題

陷阱:回調(diào)可能在不同線程中執(zhí)行,導(dǎo)致并發(fā)訪問(wèn)問(wèn)題。

class Counter {
    int count = 0;
    
public:
    void registerCallbacks() {
        // 這些回調(diào)可能在不同線程中被調(diào)用
        registerCallback([this]() { count++; });  // 不是線程安全的
        registerCallback([this]() { count++; });
    }
};

最佳實(shí)踐:

  • 使用互斥鎖保護(hù)共享數(shù)據(jù)
class ThreadSafeCounter {
    int count = 0;
    std::mutex mutex;
    
public:
    void registerCallbacks() {
        registerCallback([this]() { 
            std::lock_guard<std::mutex> lock(mutex);
            count++;  // 現(xiàn)在是線程安全的
        });
    }
};
  • 使用原子操作
class AtomicCounter {
    std::atomic<int> count{0};
    
public:
    void registerCallbacks() {
        registerCallback([this]() { 
            count++;  // 原子操作,線程安全
        });
    }
};

5. 循環(huán)引用(內(nèi)存泄漏)

陷阱:對(duì)象間相互持有回調(diào),導(dǎo)致循環(huán)引用無(wú)法釋放內(nèi)存。

class Button {
    std::function<void()> onClick;
    
public:
    void setClickHandler(std::function<void()> handler) {
        onClick = handler;
    }
};

class Dialog {
    std::shared_ptr<Button> button;
    
public:
    Dialog() {
        button = std::make_shared<Button>();
        // 循環(huán)引用: Dialog引用Button,Button的回調(diào)引用Dialog
        button->setClickHandler([this]() {
            this->handleClick();  // 捕獲了this指針
        });
    }
};

最佳實(shí)踐:使用 enable_shared_from_this

class DialogWithWeakPtr : publicstd::enable_shared_from_this<DialogWithWeakPtr> {
    std::shared_ptr<Button> button;
    
public:
    DialogWithWeakPtr() {
        button = std::make_shared<Button>();
    }
    
    void initialize() {
        // 安全地獲取this的weak_ptr
        std::weak_ptr<DialogWithWeakPtr> weakThis = shared_from_this();
            
        button->setClickHandler([weakThis]() {
            // 嘗試獲取強(qiáng)引用
            if (auto dialog = weakThis.lock()) {
                dialog->handleClick();  // 安全使用
            }
        });
    }
    
    void handleClick() {
        // 處理點(diǎn)擊事件
    }
};

// 使用方式
auto dialog = std::make_shared<DialogWithWeakPtr>();
dialog->initialize();  // 必須在shared_ptr構(gòu)造后調(diào)用

九、回調(diào)函數(shù)在現(xiàn)代C++中的演化

C++11及以后的版本為回調(diào)函數(shù)提供了更多現(xiàn)代化的實(shí)現(xiàn)方式:

1. std::function 和 std::bind

std::function是一個(gè)通用的函數(shù)包裝器,可以存儲(chǔ)任何可調(diào)用對(duì)象:

// 接受任何滿足簽名要求的可調(diào)用對(duì)象
void performOperation(std::function<int(int, int)> operation, int a, int b) {
    int result = operation(a, b);
    std::cout << "結(jié)果: " << result << std::endl;
}

// 使用
performOperation([](int x, int y) { return x + y; }, 5, 3);

2. Lambda表達(dá)式

Lambda大大簡(jiǎn)化了回調(diào)函數(shù)的編寫:

std::vector<int> numbers = {5, 3, 1, 4, 2};

// 使用lambda作為排序規(guī)則
std::sort(numbers.begin(), numbers.end(), 
          [](int a, int b) { return a > b; });

// 使用lambda作為遍歷操作
std::for_each(numbers.begin(), numbers.end(),
              [](int n) { std::cout << n << " "; });

3. 協(xié)程(C++20)

C++20引入了協(xié)程,可以更優(yōu)雅地處理異步操作:

// 注意:需要C++20支持
std::future<int> asyncOperation() {
    // 模擬異步操作
    co_return 42;  // 使用co_return返回結(jié)果
}

// 使用co_await等待異步結(jié)果
std::future<void> processResult() {
    int result = co_await asyncOperation();
    std::cout << "結(jié)果: " << result << std::endl;
}

協(xié)程將回調(diào)風(fēng)格的異步代碼轉(zhuǎn)變?yōu)楦鬃x的同步風(fēng)格,是解決回調(diào)地獄的有效方式。

十、總結(jié):回調(diào)函數(shù)的本質(zhì)與價(jià)值

經(jīng)過(guò)這一路的學(xué)習(xí),我們可以總結(jié)回調(diào)函數(shù)的本質(zhì):

  • 控制反轉(zhuǎn)(IoC) - 把"何時(shí)執(zhí)行"的控制權(quán)交給調(diào)用者
  • 延遲執(zhí)行 - 在特定條件滿足時(shí)才執(zhí)行代碼
  • 解耦合 - 分離"做什么"和"怎么做"
  • 行為參數(shù)化 - 將行為作為參數(shù)傳遞

回調(diào)函數(shù)的最大價(jià)值在于它實(shí)現(xiàn)了"控制反轉(zhuǎn)",這使得代碼更加靈活、可擴(kuò)展、可維護(hù)。這也是為什么它在GUI編程、事件驅(qū)動(dòng)系統(tǒng)、異步編程等領(lǐng)域如此重要。

最后用一句話總結(jié)回調(diào)函數(shù):把"怎么做"的權(quán)力交給別人,自己只負(fù)責(zé)"做什么"的一種編程技巧。

責(zé)任編輯:趙寧寧 來(lái)源: 跟著小康學(xué)編程
相關(guān)推薦

2010-02-04 16:07:39

C++回調(diào)函數(shù)

2018-11-29 08:00:20

JavaScript異步Promise

2023-04-18 08:10:10

2009-08-12 10:11:18

C# 回調(diào)函數(shù)

2011-06-15 11:05:14

C語(yǔ)言回調(diào)函數(shù)

2009-08-19 17:10:09

C#回調(diào)函數(shù)

2009-08-19 16:40:35

C#回調(diào)

2009-07-31 16:25:29

C#回調(diào)函數(shù)API應(yīng)用

2018-10-22 08:14:04

2020-11-03 10:32:48

回調(diào)函數(shù)模塊

2022-04-12 08:30:52

回調(diào)函數(shù)代碼調(diào)試

2011-07-25 14:27:10

Objective-C 協(xié)議 函數(shù)

2012-02-01 10:33:59

Java

2021-04-07 13:28:21

函數(shù)程序員異步

2017-03-16 15:28:20

人工智能視覺識(shí)別

2011-05-20 17:19:25

回調(diào)函數(shù)

2023-01-26 23:44:41

C++代碼生命周期

2023-12-15 09:45:21

阻塞接口

2018-01-12 14:49:18

區(qū)塊鏈分布式數(shù)據(jù)庫(kù)

2022-02-14 21:17:21

RPC框架協(xié)議
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)