我希望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
一起使用,因为几个类继承了 ftomObserver
.
输出:virtual void Observer::onNewGame()
但我希望:virtual void Npc::onNewGame()
首先,不要使用std::bind()
。它几乎已经过时了。(更喜欢 lambda。
其次,如果你直接调用它而不是std::bind()
,它将起作用:
(observer.*eventMethod)();
在Coliru上直播,其中输出是所需的:
virtual void Npc::onNewGame()
或者,如果您有 C++17 支持,则可以使用std::invoke()
,根据此C++有关间接调用成员函数的常见问题解答。
您可以考虑使用像 Boost.Signals2 这样的高质量观察器库,而不是自己创建。如果您确实需要自己动手,请考虑使用std::function
作为其基础。
PS、风格提示:
- 您不需要散布在整个代码中的所有
this->
。也许它们来自需要它们的更复杂的上下文,但正如所写,它们是多余的。 - 最好将非基类中的
virtual
替换为override
,以帮助编译器帮助您避免错误。 - 通过向不修改成员数据的函数(如
Notifier
的notifyNewGame()
和notifyAll()
(添加const
来首选const
正确性。
std::bind
不存储对对象的引用,因此您可以获得它的一部分.
立即的解决方法是再次使用std::ref
:
std::bind(eventMethod, std::ref(observer))()
但是bind
是不必要的间接寻址,您可以更直接地完成相同的操作:
(observer.*eventMethod)()
或使用std::function
:
void notifyNewGame() {
notifyAll([](Observer& o) { o.onNewGame(); } );
}
void notifyAll(std::function<void(Observer&)> fn) {
std::for_each(observers.begin(), observers.end(), fn);
}