6

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

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() 静态
  • 将机器人指针传递给任务
4

4 回答 4

5

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

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

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

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

最后,如果您使用的是 C++11,您可以使用匿名函数,通过将函数和在其上运行的对象包装在单个闭包中,无需使用外部库(例如 boost),即可非常优雅地解决问题。

这是第三种方法的快速示例(指向 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();
}
于 2012-08-08T11:02:21.423 回答
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;
}
于 2012-08-08T13:31:28.343 回答
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接口的对象

于 2012-08-08T10:52:52.567 回答
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该类。

于 2012-08-08T10:53:14.267 回答