C ++에서 콜백을 구현하는 몇 가지 우아한 방법

목차

첫째, 함수 포인터 사용

둘째, 가상 방법 구현

셋, std :: future 및 std :: async 사용


 

두 클래스 간의 비동기 시나리오를 고려하십시오. 클라이언트는 특정 작업을 처리하기 위해 클래스 A를 호출하고 A는 일부 처리를 수행하고 결과를 반환하기 위해 B를 위임해야합니다. 모두 비동기 적으로 수행됩니다. 즉, 함수가 빠르게 반환 된 다음 계산이 백그라운드에서 수행되고 알림이 완료됩니다.

첫째, 함수 포인터 사용

클래스 B는 함수 포인터를 받기 위해 SetCallBack 인터페이스를 가져야합니다. 여기서 우리는 std :: function을 사용하여 달성합니다. B. ProcessAsync 작업이 수행되면 다른 스레드가 시작되고 분리되어 더 긴 백그라운드 계산을 수행합니다 (여기서는 std :: this_thread :: sleep_for가 긴 계산을 시뮬레이션하는 데 사용됨). 계산이 완료된 후 방금 설정된 것은 발신자에게 알리는 데 사용됩니다.

#include <iostream>
#include <functional>
#include <thread>

class B
{
public:
    void SetCallBack(const std::function<void(double)> &cb) {
        m_callback = cb;
    }
    void ProcessAsync() {
        printf("class B, create thread to compute and return immediately\n");
        std::thread th(&B::ProcessReal, this);
        th.detach();
    }
    
private:
    void ProcessReal() {
        printf("class B, Process for a long time...\n");
        std::this_thread::sleep_for(std::chrono::seconds(3));
        if (m_callback) {
            printf("callback\n");
            m_callback(3.72);
        }
    }
    std::function<void(double)> m_callback;
};

클래스 A는 콜백을 클래스 B로 설정할 때 람다 식 또는 std :: bind를 사용할 수 있습니다. 이는 개인의 선호도에 따라 다릅니다. A가 콜백을 받으면 계산 결과를 인쇄합니다.

#include <iostream>
#include <memory>
#include <functional>
#include "B.h"

class A
{
public:
    void DoTask() {
        printf("class A, DoTask\n");
        m_b = std::make_shared<B>();
        //m_b->SetCallBack([this](double res) {return OnProcessDone(res);});
        m_b->SetCallBack(std::bind(&A::OnProcessDone, this, std::placeholders::_1));
        m_b->ProcessAsync();
    }
private:
    void OnProcessDone(double result) {
        printf("OnProcessDone, result = %f\n", result);
    }
    std::shared_ptr<B> m_b{nullptr};
};

주요 기능에 대해 할 말이 많지 않음

#include <thread>
#include <chrono>
#include "A.h"

int main() {
    A a;
    a.DoTask();
    std::this_thread::sleep_for(std::chrono::seconds(5));
    return 0;
}

 

둘째, 가상 방법 구현

B는 순수 가상 메서드 OnProcessDone이있는 인터페이스 클래스 Listener를 제공하고 A는 B :: Listener를 상속하고이 가상 메서드를 구현하기 위해 새 MyListener를 선언 할 수 있습니다.

여기서 A :: MyListener와 B :: Listener는 모두 내부 클래스로 선언되어 있는데, 장점은 내부 클래스가 기본적으로 외부 클래스의 친구 이기 때문에 A :: MyListener가 클래스 A의 private 멤버에 접근 할 수 있다는 것입니다. , 콜백에서이 방법은 비공개 멤버에 액세스해야 할 때 매우 편리합니다.

#include <iostream>
#include <functional>
#include <thread>
#include <memory>

class B
{
public:
    class Listener {
    public:
        virtual void OnProcessDone(double result) = 0;
    };
    void SetCallBack(const std::shared_ptr<Listener> &cb) {
        m_callback = cb;
    }
    void ProcessAsync() {
        printf("class B, create thread to compute and return immediately\n");
        std::thread th(&B::ProcessReal, this);
        th.detach();
    }
    
private:
    void ProcessReal() {
        printf("class B, Process for a long time...\n");
        std::this_thread::sleep_for(std::chrono::seconds(3));
        if (m_callback) {
            printf("callback\n");
            m_callback->OnProcessDone(3.72);
        }
    }
    std::shared_ptr<Listener> m_callback;
};
#include <iostream>
#include <memory>
#include <functional>
#include "B.h"

class A
{
public:
    void DoTask() {
        printf("class A, DoTask\n");
        m_b = std::make_shared<B>();
        m_b->SetCallBack(std::make_shared<MyListener>(this));
        m_b->ProcessAsync();
    }
private:
    class MyListener : public B::Listener {
    public:
        MyListener(A *a) : m_a(a) {}
        void OnProcessDone(double result) {
            printf("OnProcessDone, result = %f\n", result);
            m_a->m_res = result;
        }
    private:
        A *m_a;
    };
    
    std::shared_ptr<B> m_b{nullptr};
    double m_res;
};

 

셋, std :: future 및 std :: async 사용

클래스 A에서 std :: async를 시작하여 B의 동기 처리 함수를 비동기로 만들어 B에 콜백을 설정할 필요가 없으며 B 구현이 매우 간단합니다. A에서는 결과를 계산해야 할 때 future의 .get ()을 호출하여 결과를 얻을 수 있습니다.

#include <iostream>
#include <thread>

class B
{
public:
    double ProcessSync() {
        printf("class B, Process for a long time...\n");
        std::this_thread::sleep_for(std::chrono::seconds(3));
        return 3.72;
    }
};

 std :: future는 복사 할 수 없으므로 미래를 다른 스레드로 전달할 때 이동하십시오.

#include <iostream>
#include <memory>
#include <functional>
#include <future>
#include "B.h"

class A
{
public:
    void DoTask() {
        printf("class A, DoTask\n");
        m_b = std::make_shared<B>();
        std::future<double> fut = std::async(std::bind(&B::ProcessSync, m_b));
        std::thread th(&A::WaitForProcessDone, this, std::move(fut));
        th.detach();
    }
private:
    void WaitForProcessDone(std::future<double> &&fut) {
        double result = fut.get();
        printf("ProcessDone, result = %f\n", result);
        m_res = result;
    }
    std::shared_ptr<B> m_b{nullptr};
    double m_res;
};

 

추천

출처blog.csdn.net/yuejisuo1948/article/details/113280498