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プログラムにルートが1つしかなくRobot、そのロボットのインスタンスが静的に利用可能でない限り、ルートは実行可能ではありません。

タスクにaを渡すRobotことは問題ないかもしれませんが、情報が多すぎて、ロボット以外のオブジェクトでのタスクの使用が禁止される可能性があります。

3番目の方法は、完了通知用のインターフェイスのようなクラスを作成し、で拡張してRobot、タスクから呼び出すことです。残念ながら、C ++は、仮想継承領域にユーザーをプッシュすることによって、それを特に簡単にすることはできません。

POSIXスレッドライブラリで一般的なコールバックアプローチ(voidポインタとvoidポインタを受け取る関数ポインタを渡す)を採用することもできますが、それはC++っぽくはありません。

最後に、C ++ 11を使用している場合は、Boostなどの外部ライブラリを使用せずに、関数とそれが動作するオブジェクトの両方を1つのクロージャーでラップすることにより、問題に非常に優雅に対処できる無名関数があります。

3番目のアプローチの簡単な例を次に示します(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

私はポインタを渡すことで行きます。ただし、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を含む純粋な仮想基本クラスから継承し、クラスにこの仮想基本クラスへのポインターまたは参照を持たせ、含まれるクラスにインスタンスを渡すことです。クラスに基本クラスから継承させる代わりに、テンプレートパラメーターとしてテンプレート化されたクラスに渡すことができます。OnTaskCompletedTaskRobotTaskRobotTask

別の解決策は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 に答える
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 に答える