回调函数,这里说的是一种笼统的概念,泛指一个对象或者一个过程调用陷入另一个对象或过程。本文想要描述的就是在回调函数中执行释放、删除动作去销毁调用调用者相关联的内容,导致访问非法。
下面用一段代码来简述一下这个坑是怎么产生的:
class ISomeFace
{
public:
void callback(CBase* pBase)=0;
}
class CCallee:public ISomeFace
{
public:
void callback(CBase* pBase);
{
handleCBase(pBase);
if(pBase == WORK_DONE)
{
delete pBase;
}
}
}
class CCaller: public CBase
{
public:
virtual ~CCaller()
{
if(mBuffer)delete[] mBuffer;
}
void doSomething(ISomeFace* sf)
{
if(mBuffer==NULL) mBuffer = new char[1024];
sf->callback(this);
doNextActionWith(mBuffer);
}
private:
char* mBuffer;
}
当调用doSomething的时候进入了sf->callback,在callback中因为达成了某些条件,代码决定删掉pBase,也就是CCaller。可以看到~CCaller中释放了mBuffer。这样当调用完毕回到doSomething的doNextActionWith,此时访问mBuffer就成为非法访问。
这种问题比较隐晦,有时候因为mBuffer刚被释放,立即访问不会马上产生异常,或者因为doNextActionWith做的比较严谨程序长时间都不会出现问题,但是保不齐哪天开始大规模出事!
另外实际项目中,这种销毁关系不会那么明显,可能经过多层调用后辗转间接执行了销毁,特别是仅在小概率条件发生时才销毁,这样就产生了小概率崩溃发生,非常难于调试。所以我建议的解决办法是马上去review所有在回调中执行的销毁动作,最好理清间接的销毁动作;如果是linux平台可以用valgrind的memcheck来协助你找到这样的问题。