c++ - 在回调函数中安全地删除调用者对象

标签 c++ oop design-patterns

库中的代码段:

class Client{
public:
    class CallBack {
    public:
        virtual void onData(Client* caller, std::string& data) =0;
    };

    Client(CallBack* callback):m_callBack(callback){}
    virtual ~Client(){}
    void onData(std::string data) {
        m_callBack->onData(this, data);
        m_totalDataVol += data.size();
    }

private:
    CallBack* m_callBack;
    int m_totalDataVol = 0;
}

来自应用程序的代码段:

class AppHandler: public Client::Callback {
    void onData(Client* caller, std::string& data) {
        /* Some complex logic and check certain conditions*/
            delete caller; // Application will crash, due to 
                           // accessing member of deleted object (m_totalDataVol)
    }
}

此外Caller对象(Client类的实例)为应用程序所有,应用程序没有限制删除它。

我该如何克服这个问题?

非常复杂的场景: 基础库的 Client 类可以由另一个库(ClientEx 类)扩展,应用程序可能会使用该扩展库(不是基础库)

最佳答案

让你的回调返回一个 bool 指示调用者是否应该删除自己。不要从回调中删除客户端。

此外,如果 data.size == 0,是否还需要调用回调? Client 可以在调用回调之前检查此条件,并删除自身(或以其他方式适本地处理它)。

如果仍然需要调用回调,也许您可​​以通过在调用后检查客户端中的条件来避免更改返回类型:

void onData(std::string data) {
    m_callBack->onData(this, data);
    if (data.size() != 0) {
        m_totalDataVol += data.size();
    }
    else {
        delete this;
    }
}

或者,如果您真的必须允许回调来删除客户端,那么您需要某种方式来跟踪客户端何时被删除,您可以在客户端本身中使用这种方式。这意味着保留对另一个对象的引用:

class Client{
public:
    class CallBack {
    public:
        virtual void onData(Client* caller, std::string& data) =0;
    };

    Client(CallBack* callback):m_callBack(callback){}, was_deleted(nullptr)
    virtual ~Client(){
        if (was_deleted) *was_deleted = true;
    }
    void onData(std::string data) {
        bool *was_deleted = new bool();
        this->was_deleted = was_deleted;
        m_callBack->onData(this, data);
        if (! *was_deleted) {
            m_totalDataVol += data.size();
            this->was_deleted = nullptr;
        }
        delete was_deleted;
    }

private:
    CallBack* m_callBack;
    int m_totalDataVol = 0;
    // When issuing a callback, holds a pointer to a flag that can
    // be used to track if this object has been deleted:
    bool * was_deleted;
}

(请注意,上面的解决方案不是线程安全的,但可能会如此。另请注意,上面的代码无法编译,就像您问题中的示例代码一样 - 我已经尝试匹配源代码尽可能多,原则应适用于实际代码)。

关于c++ - 在回调函数中安全地删除调用者对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32472221/

相关文章:

c++ - 如何将过程/函数值获取到最终程序中?

c++ - 与自身双重比较

C++:未实现:非静态数据成员初始值设定项

java - 为什么/何时应该使用泛型方法?

java - 在处理无法修改的类时避免使用 instanceof 的最佳设计模式?

macos - 响应 keyDown 事件的最佳实践

c++ - 如何在C++中实现包含指针的STL容器的类的迭代器

php - URL Router 和 Dispatcher 有什么区别?

c# - 类,使用方法的最佳方式是什么?

rest - 休息应用程序中的观察者设计模式