gpt4 book ai didi

c++ - 创建注册回调的抽象基类的正确方法

转载 作者:搜寻专家 更新时间:2023-10-31 00:29:40 25 4
gpt4 key购买 nike

我想要一个基类,其目的是为回调注册它(并在析构函数中取消注册),回调是一个纯虚函数。像这样。

struct autoregister {
autoregister() { callback_manager.register(this); }
~autoregister() { callback_manager.deregister(this); }
virtual void call_me()=0;
};

但这对我来说似乎不可靠,我怀疑那里有几个竞争条件。 1) 当 callback_manager 看到指针时,call_me 仍然不可调用,并且在对象完成构建之前可能需要任意时间,2) 在调用 deregister 时,派生对象析构函数被调用,因此回调应该不被调用。

我在想的一件事是在 callback_manager 内部检查指针的 call_me 是否有效,但我找不到符合标准的方法来获取 call_me 的地址或任何东西。我正在考虑将 typeid(pointer) 与 typeid(autoregister*) 进行比较,但中间可能有一个抽象类,这使得它不可靠,派生:public middle {}; middle : public autoregister {};, middle 的构造函数可以花费一个小时,例如加载SQL或者google,回调看到不是基类,认为回调可以调用,轰。这能做到吗?

Q1:还有其他竞争条件吗?

Q2:如何在不要求派生类手动调用register的情况下做到正确(没有竞争条件、未定义行为和其他错误)?

Q3:如何判断指针是否可以调用虚函数?

最佳答案

您应该将回调和注册句柄分开。除了 callback_manager 之外没有人需要 call_me 方法,那么为什么要让它从外面可见呢?使用 std::function 作为回调,因为它非常方便:任何可调用对象都可以转换为它,而且 lambda 非常方便。从回调注册方法返回一个 Handle 对象。 Handle 将拥有的唯一方法是一个析构函数,您将从中删除回调。

class Handle {
public:
explicit Handle(std::function<void()> deleter)
: deleter_(std::move(deleter))
{}

~Handle()
{
deleter_();
}
private:
std::function<void()> deleter_;
};

class Manager {
public:
typedef std::function<void()> Callback;

Handle subscribe(Callback callback) {
// NOTE: use mutex here if this method is accessed from multiple threads
callbacks_.push_back(std::move(callback));
auto itr = callbacks_.end() - 1;
// NOTE If Handle lifetime can exceed Manager lifetime, store handlers_ in std::shared_ptr and capture a std::weak_ptr in lambda.
return Handle([this, itr]{
// NOTE: use mutex here if this method is accessed from multiple threads
callbacks_.erase(itr);
});
}

private:
std::list<Callback> callbacks_;
};

Q1: are there other race conditions?

Callback/Handle 可能比 callback_manager 存在时间更长,并且会尝试取消订阅已删除的对象。这可以通过策略(在删除管理器之前始终取消订阅所有内容)或使用弱指针来解决。

如果 callback_manager 从多个线程访问,则存在明显的竞争,您需要使用互斥锁来保护回调存储。

Q2: how to do this right (without race conditions, undefined behavior and other errors) without asking the derived class to call register manually?

见上文。

Q3: how to check if a virtual function can be called on a pointer?

这是不可能的。

关于c++ - 创建注册回调的抽象基类的正确方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40112648/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com