图书馆的代码段:
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类的实例)归应用程序所有,而Application没有限制删除它。
我如何克服这个问题?
非常复杂的情况:
基础库的Client
类可以由另一个库(ClientEx
类)扩展,应用程序可能使用该扩展库(不是基础库)
答案 0 :(得分:2)
让你的回调返回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;
}
(请注意,上面的解决方案不是线程安全的,但可能是这样做的。还要注意上面的代码没有编译,就像你问题中的示例代码没有 - 我有尽可能地匹配原始代码;原则应适用于实际代码。)