从包含的类调用容器类中定义的回调函数的推荐方法是什么?

时间:2012-08-08 10:49:13

标签: c++ oop

想象一下,您有一个类层次结构,如下所示:

class Robot
{
public:
    void OnTaskCompleted() {}

private:
    Task *m_pTask;
};

class Task
{
public:
    virtual void DoTask() = 0;
};

class TidyUp : public Task
{
public:
    void DoTask()
    {
        // When TidyUp task is compeleted invoke OnTaskCompleted() from here.
    }
};

我需要从OnTaskCompleted()致电TidyUp::DoTask()。建议的方法是什么?

我想避免:

  • 制作OnTaskCompleted()static
  • 将机器人指针传递给任务

4 个答案:

答案 0 :(得分:5)

static路由不可行,除非程序中只有一个Robot,并且该机器人的实例是静态可用的。

Robot传递给任务可能没问题,但可能会泄露太多信息并禁止使用机器人以外的对象进行任务使用。

第三种方法是为完成通知创建类似接口的类,在Robot中扩展它,并从任务中调用它。不幸的是,C ++并没有让你进入虚拟继承领域变得特别容易。

您可以采用POSIX线程库中常见的回调方法(传递void指针和带有void指针的函数指针),但这不是C ++ - ish。

最后,如果您正在使用C ++ 11,那么您可以anonymous functions通过在不使用外部库的情况下将函数和操作对象包装在单个闭包中来非常优雅地解决问题,比如提升。

以下是第三种方法(link to ideone)的快速示例:

#include <iostream>
#include <string>
using namespace std;

class WithNotification {
public:
    virtual void notify()=0;
};

class Robot : public virtual WithNotification {
private:
    string name;
public:
    Robot(const string& n) : name(n) {}
    virtual void notify() {cout << name << " has been notified" << endl; }
};

class Task {
private:
    WithNotification& onFinished;
public:
    Task(WithNotification& f) : onFinished(f) {}
    void run() {
        cout << "The task is running" << endl;
        onFinished.notify();
    }
};

int main() {
    Robot r1("Quick");
    Robot r2("Brown");
    Task t1(r1);
    Task t2(r2);
    t1.run();
    t2.run();
}

答案 1 :(得分:0)

我会去传递一个指针。但是,如果您计划Task使用Robot,请考虑使用TaskDelegate接口发送通知:

class TaskDelegate
{
public:
    virtual void onTaskFinished(Task *sender) = 0; //implement in successors

protected:
    ~TaskDelegate() {} //don't allow deletion via interface pointer
};

现在,您的Task课程将如下所示:

class Task
{
    Task(TaskDelegate *delegate) {...}
    ...
};

Robot继承为delegate

class Robot : public TaskDelegate {...}

因此Task能够通知任何实现TaskDelegate接口

的对象

答案 2 :(得分:0)

如果没有将任何内容传递给包含的类,就无法解决这个问题。最简单的方法是让Robot继承自包含OnTaskCompleted方法的纯虚基类,并使Task类具有指向此虚拟基类的指针或引用,并传递包含Robot类的Task实例。您可以将其作为模板参数传递给模板Robot类,而不是让Task类继承自基类。

另一种解决方案是使Task成为模板类,并将this->OnTaskCompleted函数作为模板参数传递。类似的东西:

template<class Callback>
class Task
{
public:
    void DoTask()
    {
        // ...

        Callback();
    }
};

class Robot
{
public:
    Robot()
    {
        m_pTask = new Task<this->OnTaskCompleted>;
    }

    // ...
};

如果这最后一种方式有效,我实际上不知道,因为我没有尝试过。

正如您所看到的,无论是我和其他答案,都有很多方法可以做到这一点。它们都不是实际的“官方”或“推荐”方式。但是几乎所有这些都需要您将类或对象传递给Task类。

答案 3 :(得分:0)

根据Xeo的建议,我做了类似的事情:

#include <iostream>
#include <functional>

typedef std::tr1::function<void (int)> CallbackFunc;

class Task
{
public:
    Task(CallbackFunc callbackFunc) : m_callbackFunc(callbackFunc) {}
    virtual ~Task() {}
    virtual void DoTask() = 0;
protected:
    CallbackFunc m_callbackFunc; 
};

class TidyUp : public Task
{
public:
    TidyUp(CallbackFunc callbackFunc) : Task(callbackFunc) {}
    void DoTask() {
        std::cout << "I love tidying up!" << std::endl;
        m_callbackFunc(6); // When TidyUp task is compeleted invoke OnTaskCompleted() from here.
    }
};

class Robot : private Uncopyable
{
public:
    Robot() : m_pTask(new TidyUp(std::bind(&Robot::OnTaskCompleted, this, std::placeholders::_1))) {}
    ~Robot() { delete m_pTask; }
    void DoTask() { m_pTask->DoTask(); }
    void OnTaskCompleted(int nCleannessLevel) { std::cout << "Done!  Cleanness Level: " << nCleannessLevel << std::endl; }
private:
    Task *m_pTask;
};

int main()
{
    Robot robot;
    robot.DoTask();
    return 0;
}