调用std :: function对象指向deallocated对象的方法

时间:2015-01-22 02:04:46

标签: c++ c++11 function-pointers std

考虑以下代码:

#include <iostream>
#include <functional>

using namespace std;
using namespace std::placeholders; 

typedef function<void(const int&)> SomeFunc;

class X {
public:
    X(string name):name_(name)
    { cout << "ctor " << name_ << endl; }

    ~X()
    {
        cout << "dtor " << name_ << endl; 
        name_ = "empty";
    }

    SomeFunc
    getSomeFunc()
    { return bind(&X::someMethod, this, _1); }

private:
    string name_;

    void
    someMethod(const int& a)
    {
        cout << name_ << " some method with " << a << endl;
    }
};


int main()
{
    SomeFunc f;

    {
        shared_ptr<X> x(new X("Object"));
        f = x->getSomeFunc();
        f(1);
    }

    f(2);

    return 0;
}

有时,输出会给我这个:

ctor Object
Object some method with 1
dtor Object
empty some method with 2

其他时候:

ctor Object
Object some method with 1
dtor Object
 some method with 2

在现实世界中,一旦解除分配的对象试图访问它的属性,它很可能会让我崩溃。 所以这里有一个问题 - 因为函数不保证持有对象引用的引用,在引用的对象已被释放后调用函数时,避免崩溃的最佳做法是什么?

我可能会想到的解决方案之一 - 在对象内维护一个特殊的标志bool deallocated_并在方法内部检查它,这可能在解除分配后被调用。但是,我怀疑,它也不可靠。

更新(来自评论):

我需要此解决方法的真正原因是将函数作为参数的库。这个库是异步操作的,我无法控制传递给它的函数对象。这就是为什么当我的对象被释放时,库仍然可以使用最初传递的函数来调用回调,这会导致崩溃。

4 个答案:

答案 0 :(得分:3)

您的对象由shared_ptr持有,因此您可以使用lambda来关闭shared_ptr

auto func = [ptr](const int &p){ ptr->someMethod(p); };

您需要使用shared_from_this在课堂上获取ptr

这是一个有效的完整示例:

#include <iostream>
#include <functional>
#include <memory>

using namespace std;
using namespace std::placeholders; 

typedef function<void(const int&)> SomeFunc;

class X : public enable_shared_from_this<X> {
public:
    X(string name) : name_(name) {
        cout << "ctor " << name_ << endl;
    }

    ~X() {
        cout << "dtor " << name_ << endl; 
        name_ = "empty";
    }

    SomeFunc getSomeFunc() {
        auto ptr = shared_from_this();
        return [ptr](const int &a){ ptr->someMethod(a); };
    }

private:
    string name_;

    void someMethod(const int& a) {
        cout << name_ << " some method with " << a << endl;
    }
};


int main()
{
    SomeFunc f;

    {
        shared_ptr<X> x(new X("Object"));
        f = x->getSomeFunc();
        f(1);
    }

    f(2);

    return 0;
}

输出如下:

ctor Object
Object some method with 1
Object some method with 2
dtor Object

答案 1 :(得分:1)

您可以创建一个包含函数指针和shared_ptr的对象的类。对象的shared_ptr保证在破坏函数类之前不会销毁对象。

答案 2 :(得分:1)

Sulution 1)使用weak_ptr + lambda(几乎与 b4hand 相同,但它不会强迫你的班级活着)

从std :: enable_shared_from_this

继承你的类
class X : public enable_shared_from_this<X>

并将getSomeFunc更改为以下内容:

SomeFunc getSomeFunc()
{
    weak_ptr<X> weak = shared_from_this();

    return [weak, this](const int& a){
        shared_ptr<X> shared = weak.lock();

        if (shared)
        {
            this->someMethod(a);
        }
    };
}

输出:

ctor Object
Object some method with 1
dtor Object

更多详情herehere

解决方案2)有点疯狂的代码+ lambda
如果您不能或不想使用共享/弱ptrs,您可以这样做:

#include <memory>
#include <functional>
#include <iostream>
#include <memory>
#include <string>
#include <set>

using namespace std;

typedef function<void(const int&)> SomeFunc;

class X {
private:
    static set<X*> _aliveInstanties;
public:
    X(string name) :name_(name)
    {
        _aliveInstanties.insert(this);

        cout << "ctor " << name_ << endl;
    }

    ~X()
    {
        _aliveInstanties.erase(_aliveInstanties.find(this));

        cout << "dtor " << name_ << endl;
        name_ = "empty";
    }

    SomeFunc getSomeFunc()
    {
        return [this](const int& a)
        {
            if (_aliveInstanties.find(this) != _aliveInstanties.end())
            {
                this->someMethod(a);
            }
        };
    }

private:
    string name_;

    void someMethod(const int& a)
    {
        cout << name_ << " some method with " << a << endl;
    }
};

答案 3 :(得分:0)

不使用lambda的另一个解决方案是从enable_shared_from_this派生并在shared_from_this方法中传递getSomeFunc

class X : public enable_shared_from_this<X> {
public:
    X(string name):name_(name)
    { cout << "ctor " << name_ << endl; }

    ~X()
    {
        cout << "dtor " << name_ << endl; 
        name_ = "empty";
    }

    SomeFunc
    getSomeFunc()
    { 
        return bind(&X::someMethod, shared_from_this(), _1); 
    }

private:
    string name_;

    void
    someMethod(const int& a)
    {
        cout << name_ << " some method with " << a << endl;
    }   
};

但是,这将保留对象,直到释放所有回调。