简体   繁体   English

如何在C ++ 11 lambda中跟踪对象的生命周期?

[英]How can I track object lifetime in C++11 lambda?

Sometimes, we know nothing about lifetime of lambda that captures an object state (eg return it from object, register it as a callback without ability to unsubscribe etc.). 有时,我们对捕获对象状态的lambda的生命周期一无所知(例如,将其从对象返回,将其注册为回调而无需取消订阅等)。 How to make sure that the lambda won't access already destroyed object on invocation? 如何确保lambda在调用时不会访问已销毁的对象?

#include <iostream>
#include <memory>
#include <string>

class Foo {
public:
    Foo(const std::string& i_name) : name(i_name) {}

    std::function<void()> GetPrinter() {
        return [this]() {
            std::cout << name << std::endl;
        };
    }

    std::string name;
};

int main() {
    std::function<void()> f;

    {
        auto foo = std::make_shared<Foo>("OK");
        f = foo->GetPrinter();
    }

    auto foo = std::make_shared<Foo>("WRONG");

    f();

    return 0;
}

This program prints "WRONG" instead of "OK" ( http://ideone.com/Srp7RC ) just by coincidence (it seems it just reused the same memory for the second Foo object). 这个程序只是巧合地打印出“WRONG”而不是“OK”( http://ideone.com/Srp7RC )(似乎它只是为第二个Foo对象重用了相同的内存)。 Anyway, this is a wrong program. 无论如何,这是一个错误的计划。 First Foo object is already dead when we execute f . 当我们执行f时,第一个Foo对象已经死了。

Extend object lifetime 延长对象的生命周期

Lambdas can capture shared pointers to this , so an object won't die while at least one lambda exists. Lambdas可以捕获this共享指针,因此当至少存在一个lambda时,对象不会死亡。

class Foo : public std::enable_shared_from_this<Foo> {
public:
    Foo(const std::string& i_name) : name(i_name) {}

    std::function<void()> GetPrinter() {
        std::shared_ptr<Foo> that = shared_from_this();

        return [that]() {
            std::cout << that->name << std::endl;
        };
    }

    std::string name;
};

http://ideone.com/Ucm2p8 http://ideone.com/Ucm2p8

Usually, it is not a good solution, as object lifetime is extended in very implicit manner here. 通常,这不是一个好的解决方案,因为对象生命周期在这里以非常隐式的方式扩展。 It is very easy way of producing circular references between objects. 这是在对象之间生成循环引用的非常简单的方法。

Track object lifetime 跟踪物体寿命

Lambdas can track captured object lifetime and use the object only if it is still alive. Lambdas可以跟踪捕获的对象生命周期,并仅在对象仍然存活时使用该对象。

class Foo : public std::enable_shared_from_this<Foo> {
public:
    Foo(const std::string& i_name) : name(i_name) {}

    std::function<void()> GetPrinter() {
        std::weak_ptr<Foo> weak_this = shared_from_this();

        return [weak_this]() {
            auto that = weak_this.lock();
            if (!that) {
                std::cout << "The object is already dead" << std::endl;
                return;
            }

            std::cout << that->name << std::endl;
        };
    }

    std::string name;
};

http://ideone.com/Wi6O11 http://ideone.com/Wi6O11

Track object lifetime without shared pointers 跟踪对象生存期而不使用共享指针

As hvd noted , we can't always be sure that an object is managed by shared_ptr . 正如hvd 所说 ,我们不能总是确定一个对象是由shared_ptr管理的。 In such case, I would suggest using the following lifetime_tracker . 在这种情况下,我建议使用以下lifetime_tracker It is self-contained and doesn't affect the way you manage object lifetime. 它是自包含的,不会影响您管理对象生存期的方式。

struct lifetime_tracker
{
private:
    struct shared_state
    {
        std::uint32_t count : 31;
        std::uint32_t dead  : 1;
    };

public:
    struct monitor
    {
        monitor() : state(nullptr) {}

        monitor(shared_state *i_state) : state(i_state) {
            if (state)
                ++state->count;
        }

        monitor(const monitor& t) : state(t.state) {
            if (state)
                ++state->count;
        }

        monitor& operator=(monitor t) {
            std::swap(state, t.state);
            return *this;
        }

        ~monitor() {
            if (state) {
                --state->count;
                if (state->count == 0 && state->dead)
                    delete state;
            }
        }

        bool alive() const {
            return state && !state->dead;
        }

    private:
        shared_state *state;
    };

public:
    lifetime_tracker() : state(new shared_state()) {}
    lifetime_tracker(const lifetime_tracker&) : state(new shared_state()) {}
    lifetime_tracker& operator=(const lifetime_tracker& t) { return *this; }

    ~lifetime_tracker() {
        if (state->count == 0)
            delete state;
        else
            state->dead = 1;
    }

    monitor get_monitor() const {
        return monitor(state);
    }

private:
    shared_state *state;
};

Example of usage 用法示例

class Foo {
public:
    Foo(const std::string& i_name) : name(i_name) {}

    std::function<void()> GetPrinter() {
        auto monitor = tracker.get_monitor();

        return [this, monitor]() {
            if (!monitor.alive()) {
                std::cout << "The object is already dead" << std::endl;
                return;
            }

            std::cout << this->name << std::endl;
        };
    }

private:
    lifetime_tracker tracker;

    std::string name;
};

Stas's answer is good when you can be certain that objects are managed by a shared_ptr , but that isn't always possible. 当您可以确定对象由shared_ptr管理时,Stas的答案很好,但这并不总是可行的。 What you can always do, though, is track the lifetime of objects, and add an assertion in your lambda. 但是,你总能做的是跟踪对象的生命周期,并在lambda中添加一个断言。

void ignore(void *) { }

class Foo {
public:
    Foo(const std::string& i_name) : name(i_name) {}
    Foo(const Foo& other) : name(other.name) {}
    Foo(Foo&& other) : name(std::move(other.name)) {}
    Foo& operator=(Foo other) { swap(*this, other); return *this; }
    friend void swap(Foo& a, Foo& b) { using std::swap; swap(a.name, b.name); }

    std::function<void()> GetPrinter() {
        std::weak_ptr<void> monitor = this->monitor;

        return [=]() {
            assert (!monitor.expired());
            std::cout << name << std::endl;
        };
    }

    std::string name;

private:
    std::shared_ptr<void> monitor{this, ignore};
};

In the constructor, the shared pointer monitor is not explicitly initialised, but set up through its initialiser to point to this , and to do nothing once the pointer's lifetime expires. 在构造函数中,共享指针monitor未显式初始化,而是通过其初始化器设置为指向this ,并且在指针的生存期到期后不执行任何操作。 The idea isn't to make shared_ptr responsible for freeing the object, the idea is only to let shared_ptr pass along information on the object's lifetime. 我们的想法不是让shared_ptr负责释放对象,这个想法只是让shared_ptr传递有关对象生命周期的信息。

Prior to the creation of your lambda, you can construct a weak_ptr which tracks the associated shared_ptr . 在创建lambda之前,您可以构造一个跟踪关联的shared_ptrweak_ptr If the object has been destroyed, then its monitor member will necessarily also have been destroyed, and that becomes visible through the expired() function. 如果对象已被销毁,那么它的monitor成员也必然已被销毁,并且通过expired()函数可以看到它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM