C++,函数指针与观察者模式
C++, functions pointers vs observer pattern
几年前,在我对 C++ 了解不多之前,我曾与一位经验丰富的程序员同事讨论创建事件系统等问题。
他似乎强调需要传递函数指针,以便可以进行回调以通知 classes 状态更改等
然而,这些天每当我需要实现某种事件系统时,我只是构造一个伪接口 class,继承它并使用 subscriber/observer 模式来覆盖方法和分发事件.
前几天我在想,到目前为止,我很少需要使用函数指针,当然不是针对上述情况。我真正必须使用函数指针的唯一一种情况是在与其他人的 .dll 交互时明确要求传递一个函数指针。
我什么时候应该使用函数指针而不是 subscriber/observer 模式?
good/bad 练习使用其中之一吗? (大概在某些情况下观察者模式并不比函数指针好)。
有人可以分享一些见解吗?在这方面获得更多经验会很好吗? (上述同事已经跳槽到另一家公司,我们已经失去联系)。我一直在为这个问题绞尽脑汁,想不出什么时候使用观察者模式和接口会更整洁。
谢谢!
class 的示例我经常发现自己在写作(未经测试而写,完全脱离了我的头脑,可能实际上行不通):
class NotifierListener {
friend class Notifier;
private:
vector<NotifierListener*> mListeners;
protected:
void subscribe(NotifierListener* _subscriber) {mListeners->push_back(_subscriber);}
virtual void onNotification();
}
class Notifier : public NotifierListener {
private:
void doSomethingAndNotify() {
...
if (mListeners.size() > 0) {
for (int i = 0; i < mListeners.size(); ++i) {
mListeners[i]->onNotification();
}
}
}
}
class Something : public NotifierListener {
Something() { subscribe(this); }
void onNotification() {
cout << "I got notified\n" << endl;
}
}
正如 wasthishelpful 所指出的,继承和函数指针是实现观察者模式的两种方式。
使用函数指针(或者特别是像 C++11 的 std::function
)的主要优点是增加了灵活性。观察class不需要实现接口,调用的成员函数可以任意命名。您甚至可以调用您不能或不想修改的独立函数或对象的成员,以便它实现您的观察者接口。
另一方面,使用继承更简单直接。但除此之外,我想不出比函数指针方法有任何优势。
函数指针更有效。您现在将指针传递给接口。接口通常*包含一个指向虚表的指针。而那个 vtable 又包含函数指针。这是 3 层间接,参考位置很差。
[*] 其他实现也是可能的,但具有类似的开销。
在Observer Pattern
中,侦听器是将对接收到的事件采取某些操作的实体。大多数时候,这些实体执行各自的工作并侦听事件并调用可能不同的操作,具体取决于它们在接收事件时所处的状态。所以更好的选择是具体对象应该是听众,因为对象有独立的状态。虽然使用纯函数(将使用函数指针调用)维护这样的状态 cohesively/seamlessly 有点困难。
几年前,在我对 C++ 了解不多之前,我曾与一位经验丰富的程序员同事讨论创建事件系统等问题。 他似乎强调需要传递函数指针,以便可以进行回调以通知 classes 状态更改等
然而,这些天每当我需要实现某种事件系统时,我只是构造一个伪接口 class,继承它并使用 subscriber/observer 模式来覆盖方法和分发事件.
前几天我在想,到目前为止,我很少需要使用函数指针,当然不是针对上述情况。我真正必须使用函数指针的唯一一种情况是在与其他人的 .dll 交互时明确要求传递一个函数指针。
我什么时候应该使用函数指针而不是 subscriber/observer 模式?
good/bad 练习使用其中之一吗? (大概在某些情况下观察者模式并不比函数指针好)。
有人可以分享一些见解吗?在这方面获得更多经验会很好吗? (上述同事已经跳槽到另一家公司,我们已经失去联系)。我一直在为这个问题绞尽脑汁,想不出什么时候使用观察者模式和接口会更整洁。
谢谢!
class 的示例我经常发现自己在写作(未经测试而写,完全脱离了我的头脑,可能实际上行不通):
class NotifierListener {
friend class Notifier;
private:
vector<NotifierListener*> mListeners;
protected:
void subscribe(NotifierListener* _subscriber) {mListeners->push_back(_subscriber);}
virtual void onNotification();
}
class Notifier : public NotifierListener {
private:
void doSomethingAndNotify() {
...
if (mListeners.size() > 0) {
for (int i = 0; i < mListeners.size(); ++i) {
mListeners[i]->onNotification();
}
}
}
}
class Something : public NotifierListener {
Something() { subscribe(this); }
void onNotification() {
cout << "I got notified\n" << endl;
}
}
正如 wasthishelpful 所指出的,继承和函数指针是实现观察者模式的两种方式。
使用函数指针(或者特别是像 C++11 的 std::function
)的主要优点是增加了灵活性。观察class不需要实现接口,调用的成员函数可以任意命名。您甚至可以调用您不能或不想修改的独立函数或对象的成员,以便它实现您的观察者接口。
另一方面,使用继承更简单直接。但除此之外,我想不出比函数指针方法有任何优势。
函数指针更有效。您现在将指针传递给接口。接口通常*包含一个指向虚表的指针。而那个 vtable 又包含函数指针。这是 3 层间接,参考位置很差。
[*] 其他实现也是可能的,但具有类似的开销。
在Observer Pattern
中,侦听器是将对接收到的事件采取某些操作的实体。大多数时候,这些实体执行各自的工作并侦听事件并调用可能不同的操作,具体取决于它们在接收事件时所处的状态。所以更好的选择是具体对象应该是听众,因为对象有独立的状态。虽然使用纯函数(将使用函数指针调用)维护这样的状态 cohesively/seamlessly 有点困难。