删除回调函数中的对象

时间:2012-05-22 19:00:44

标签: c++ oop pointers memory-management dangling-pointer

我发送HTTP请求以保存/更新服务器上的数据。请求是异步进行的,并在完成时调用回调函数。一切正常,但有时,应用程序在回调中崩溃。

这就是我在做的事情:

user = new User();
user->saveOnServer();
user->zombie = true;  // Mark the user that it needs to be deleted in the callback.

User中,我有saveOnServer()方法:

void User::saveOnServer(){
    Request *request = new Request();

    // Send request to the server and register the callback.
    request ->setCallback(&userCallback, (void*)this);
}

回调:

void userCallback(void *data){
    User *user = (User*)data;

    // Do something here.
    // Delete user if it's a zombie.
    if(user->zombie)
        delete user;
}

有时,我需要在向服务器发送请求后创建新用户:

user = new User();
user->saveOnServer();
user->zombie = true;
// Some code comes here.
if(user)
    delete user;
user = new User();

问题在于,在这种情况下,应用程序在删除回调中的用户时会崩溃,因为它已被删除。另一个问题是回调删除了用户,但user中的main指针仍然指向某个地址(悬空指针),所以我再次尝试删除它。

我不确定在这种情况下管理内存的最佳方法是什么。我有zombie,因为有些情况下我不希望回调删除用户。

1 个答案:

答案 0 :(得分:3)

一旦您调用了僵尸用户saveOnServer,该请求就是该用户对象的有效“所有者”。不要自己释放它,因为还有其他东西仍然打算使用它并在以后删除它。

实际上,如果服务器操作可以异步返回,则用户对象可能随时被销毁。您应该完全停止使用其他代码。您已将该对象的控制权授予该请求,您必须停止在其他任何地方使用该对象:

user = new User();
user->zombie = true; // set *before* transferring ownership to server
user->saveOnServer();
user = NULL;
//some code comes here
user = new User();

如果您不希望请求再次使用该对象,那么您需要提供一些“取消”服务器上保存操作的工具,以便它不使用该对象。


另一种选择是使用智能指针。在主代码中,将对象存储在shared_ptr中。在请求对象中,将其存储在weak_ptr中。这样,如果您的主代码想要销毁用户对象,它只需调用user.reset()即可。然后,如果回调尝试使用weak_ptr,它将发现指向的对象不再可用。使用智能指针时, 函数都不应使用delete。指针对象将为您管理用户的生命周期。

shared_ptr<User> user = make_shared<User>()
user->saveOnServer();
//some code comes here
user.reset(new User());

saveOnServer函数中,使用shared_from_this为对象创建weak_ptr

void User::saveOnServer(){
  Request *request = new Request();

  //send request on server and register the callback
  weak_ptr<User> self(shared_from_this());
  request ->setCallback(&userCallback, self);
}

在回调中,使用weak_ptr

void userCallback(weak_ptr<User> data){
  shared_ptr<User) user = data.lock();
  if (!user)
    return;

  //do something here
}