我能够编译以下代码,其中我将"回调"传递给对象(表)。我现在要做的是在表中,调用事件侦听器中定义的句柄方法
#include <iostream>
#include <vector>
#include <memory>
class Table {
public:
struct Listener{
virtual void handle(int i) = 0;
};
std::vector<std::unique_ptr<Listener>> listeners_;
void add_listener(std::unique_ptr<Listener> l){
listeners_.push_back(std::move(l));
}
};
struct EventListener: public Table::Listener {
void handle(int e){
std::cout << "Something happened! " << e << " n";
}
};
int main(int argc, char** argv)
{
Table table;
std::unique_ptr<EventListener> el;
table.add_listener(std::move(el));
return 0;
}
编辑****
这就是我在表格中尝试的。它会导致分段错误:
for (auto t =0; t < (int)listeners_.size(); ++t) {
listeners_[t]->handle(event);
}
它不起作用,因为您从未创建过要调用的对象,只是一个指针。矢量内的指针将被nullptr
,因此对其调用函数将崩溃。 unique_ptr
与这个问题完全无关。
一半的问题是Table
无法处理nullptr
但不检查它,另一半问题是Table
无法处理nullptr
但无论如何main
传递一个。
迭代代码根本不是问题。
正如小狗的回答中提到的那行
std::unique_ptr<EventListener> el;
创建一个空std::unique_ptr
。这会导致代码侦听器稍后取消引用nullptr
。
示例的简单修复是创建一个侦听器并使用它创建unique_ptr
时:
struct EventListener: public Table::Listener {
void handle(int e){
std::cout << "Something happened! " << e << " n";
}
};
// in main()
std::unique_ptr<NoOpListener> el{ new NoOpListener };
table.add_listener(std::move(el));
如注释中所述,您的代码应确保 nullptr是不允许的。一种方法是添加检查add_listener
中的 nullptr 并引发异常或静默忽略他们。第一种选择是两者中更好的解决方案,因为它向调用方发出信号,指示有问题。
但我不明白你为什么要将听众存储在std::unique_ptr
s中。std::unique_ptr
的用途是所有权。我不明白为什么观察到的实例应拥有侦听器。还有另一种选择我认为更好;使用 std::function<>()
并按值传递它。这不允许使用nullptr
,并具有接受的额外好处不仅是函数对象,还有普通函数和 lambda,如图所示在以下代码中:
#include <iostream>
#include <vector>
#include <memory>
#include <functional>
class Table {
public:
std::vector<std::function<void(int)>> listeners_;
void add_listener(std::function<void(int)> l) {
listeners_.push_back(l);
}
void invoke_listeners(int event)
{
for(auto l : listeners_) {
l(event);
}
}
};
struct NoOpListener {
void operator() (int i) {
std::cout << "NoOpListener::operator()(" << i << ")" << std::endl;
}
};
void cb(int i) {
std::cout << "cb(" << i << ")" << std::endl;
}
int main(int argc, char** argv)
{
Table table;
table.add_listener(NoOpListener{});
table.add_listener(cb);
table.add_listener([](int i) { std::cout << "[lambda](" << i << ")" << std::endl; });
table.invoke_listeners(10);
return 0;
}
注意:如您所见,我还使用了 C++11 ranged-for 构造进行迭代在听众之上。