就是这样,我需要做一个系统来订阅事件来处理IP数据包。
描述了监听器的基类,也描述了事件类:
听众:
namespace odin::net::sniffers::listeners {
class ip {
public:
ip(ip const &) = delete;
ip(ip &&) = delete;
ip &operator=(ip const &) = delete;
ip &operator=(ip &&) = delete;
virtual void received(packets::ip const &packet) = 0;
virtual ~ip();
protected:
ip();
};
}
事件:
namespace odin::net::sniffers {
class ip;
}
namespace odin::net::sniffers::events {
class ip {
public:
void operator+=(const std::weak_ptr<listeners::ip>& lis);
void operator-=(const std::weak_ptr<listeners::ip>& lis);
protected:
void post(packets::ip const &data);
void remove_expired();
private:
friend class sniffers::ip;
std::vector<std::weak_ptr<listeners::ip>> m_listeners_;
};
}
现在,现在我可以+=
通过调用运算符来订阅事件,但这不是重点,我感兴趣的是在调用事件之前从向量中删除无效指针。
事件类代码:
namespace odin::net::sniffers::events {
void ip::operator+=(std::weak_ptr<listeners::ip> const& lis) {
m_listeners_.push_back(lis);
}
void ip::operator-=(const std::weak_ptr<listeners::ip>& lis) {
m_listeners_
.erase(std::ranges::find_if(m_listeners_,
[&](auto const &cur) {
return cur.lock() == lis.lock();
}));
}
void ip::post(packets::ip const &data) {
remove_expired();
for (const std::weak_ptr<listeners::ip>& listener : m_listeners_) {
listener.lock()->received(data);
}
}
bool expired_ptr(std::weak_ptr<listeners::ip> const& ptr) {
return ptr.expired();
}
void ip::remove_expired() {
m_listeners_
.erase(std::ranges::find_if(m_listeners_, expired_ptr));
}
}
最有趣的地方是remove_expired
那里是否一切正常,例如,当它被调用时,迭代器将无效并且不会删除找到的指针?
如果有 0 或 2+ 个无效指针,您认为会发生什么?如果 2+,则不会全部删除,如果 0 -
find
返回.end()
,则将其转移到.erase()
调用UB
。最好使用
std::erase_if(m_listeners_, expired_ptr);
,它将删除任意数量的无效指针,或者如果没有,则什么也不做。