提问者:小点点

当存在重写时调用基虚拟“绑定到对象”函数


我希望NotifyAll将重写的子方法绑定到作为父引用存储的对象。 尽管继承,基方法仍然执行。 编译器似乎看不到重载,或者将向量元素视为非引用对象。 我在下面的代码中找不到我的坏处:

#include <iostream>
#include <functional>
#include <vector>

class Observer {
public:
    virtual void onNewGame() {std::cout << __PRETTY_FUNCTION__ << '\n';}
};

class Npc: public Observer {
public:
    virtual void onNewGame() {std::cout << __PRETTY_FUNCTION__ << '\n';}
};

class Notifier {
public:
    void attach(Observer & observer) {
        this->observers.push_back(std::ref(observer));
    }
    void notifyNewGame() {
        this->notifyAll(&Observer::onNewGame);
    }
    void notifyAll(void(Observer::*eventMethod)(void)) {
        for (Observer & observer : this->observers)
            std::bind(eventMethod, observer)();
    }
    std::vector <std::reference_wrapper <Observer>> observers;
};

int main() {
    Npc npc;
    Notifier notifier;
    notifier.attach(npc);
    notifier.notifyNewGame();
}

我无法将std::bind&npc::OnNewGame一起使用,因为有几个类继承了观察者
输出:
Virtual void Observer::OnNewGame()
但我希望:
Virtual void Npc::OnNewGame()


共2个答案

匿名用户

std::bind不存储对该对象的引用,因此您将获得该对象的一部分。
立即的修复方法是再次使用std::ref:

std::bind(eventMethod, std::ref(observer))()

但是绑定是不必要的间接操作,您可以更直接地完成同样的操作:

(observer.*eventMethod)()

或者使用std::function:

void notifyNewGame() {
    this->notifyAll([](Observer& o) { o.onNewGame(); } );
}

void notifyAll(std::function<void(Observer&)> fn) {
     std::for_each(observers.begin(), observers.end(), fn); 
}

匿名用户

首先,不要使用std::bind()。 它几乎已经过时了。 (优选lambdas.)

其次,如果您直接调用它或使用std::invoke()而不是通过std::bind()调用它,它将工作:

(observer.*eventMethod)();

coliru上查看live,其中输出是所需的:

virtual void Npc::onNewGame()

另外,如果您支持C++17,则可以使用std::invoke()间接调用成员函数。

您可以考虑使用像Boost.Signals2这样的高质量观察者库,而不是使用自己的观察者库。 如果确实需要滚动您自己的代码,请考虑使用std::function作为其基础。

PS,风格提示:

  1. 您不需要贯穿整个代码的所有this->。 也许它们来自需要它们的更复杂的上下文,但正如所写的那样,它们是多余的。
  2. 倾向于用override替换非基类中的virtual,以帮助编译器帮助您避免错误。
  3. 首选常量-正确性,方法是将常量添加到不修改成员数据的notifierNotifyNewGame()NotifyAll()等函数。

相关问题