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

Jan*_*aka 6 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;
}
Run Code Online (Sandbox Code Playgroud)

应用程序的代码段:

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

另外,Caller对象(Client类的实例)由应用程序拥有,而Application没有限制删除它.

我该如何克服这个问题?

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

dav*_*mac 2

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

另外,是否需要调用回调 if 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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;
}
Run Code Online (Sandbox Code Playgroud)

(请注意,上面的解决方案不是线程安全的,但可能可以这样做。另请注意,上面的代码无法编译,就像您问题中的示例代码不能编译一样 - 我尝试将原始代码匹配为尽可能好;该原则应该适用于实际代码)。