enable_shared_from_this与直接分配

时间:2016-07-07 06:35:24

标签: c++ c++11

为什么我应该使用enable_shared_from_this,因为我也可以通过普通赋值获得相同的效果。

struct A : std::enable_shared_from_this<A> {
std::shared_ptr<A> getptr() {
    return shared_from_this();
 } 
};


int main () {
// What is the differentce between this code
 std::shared_ptr<A> p1 = make_shared<A>();
 std::shared_ptr<A> p2 = p1->getptr();

// Vs this

 std::shared_ptr<A> p1 = make_shared<A>();
 std::shared_ptr<A> p2 = p1;
}

4 个答案:

答案 0 :(得分:4)

因为你不能得到“相同”的效果“,至少不是你想到的那个。

发布的代码方法没有区别,正是因为A继承自std::enable_shared_from_this<A>。 p1和p2都是shared_ptr对象,引用相同的具体对象(假设只有一个部分是为你的测试编译的,否则你在重用id名称时会出错)。

std::enable_shared_from_this<T>允许您从某个对象获取std::shared_ptr<T>,该对象由某些预先存在的std::shared_ptr<T>类型T或其衍生物正式管理,位于您没有{的位置{1}}以另外获得的对象,但由于某种原因需要一个。例如:

std::shared_ptr<T>

在上面的示例中,#include <iostream> #include <memory> struct A; void foo(std::shared_ptr<A> arg) { } struct A : std::enable_shared_from_this<A> { void method() { foo(shared_from_this()); } }; int main () { auto a = std::make_shared<A>(); a->method(); } 需要foo作为参数。从std::shared_ptr<A>的主体开始,如果没有A::method()作为基础,则不存在此类机制。如果没有std::enable_shared_from_this<A>基础,则必须提供另一种机制,将std::enabled_shared_from_this<T>共享指针传递到调用链,直到达到a。简而言之,它看起来像这样:

foo

这显然是可怕和可怕的。此外,#include <iostream> #include <memory> struct A; void foo(std::shared_ptr<A> arg) { } struct A { void method(std::shared_ptr<A> me) { foo(me); } }; int main () { std::shared_ptr<A> a = std::make_shared<A>(); a->method(a); } 无法保证me实际上是method std::shared_ptr<T>。因此,标准委员会用this祝福我们。

答案 1 :(得分:1)

共享此功能使您可以获得shared_ptrshared_ptr - 托管对象,只要您拥有的是原始指针或引用。

直接从原始指针创建shared_ptr将创建一个新的,不相关的引用计数器。

答案 2 :(得分:1)

可能值得一提的是shared_from_this是'。'

最常见的用例是在某些异步进程运行时“让自己保持活力”。一个很好的例子是完成处理程序,另一个是'this'的回调,当它由shared_ptr控制时。

例如:

#include <memory>
#include <future>
#include <thread>
#include <chrono>
#include <iostream>

using namespace std::literals;

template<class Handler>
void long_process_with_completion_handler(Handler done)
{
    std::thread([done] {
        std::cout << "long process starts" << std::endl;
        std::this_thread::sleep_for(2000ms);
        done();
    }).detach();
}


struct controller : std::enable_shared_from_this<controller>
{
    auto get_lock() const {
        return std::unique_lock<std::mutex>(_mutex);
    }

    void start() {
        long_process_with_completion_handler([self = shared_from_this()] {
            auto lock = self->get_lock();
            std::cout << "all complete" << std::endl;
        });
    }

    mutable std::mutex _mutex;
};


int main()
{
    std::condition_variable controller_done;
    std::mutex done_mutex;
    bool is_controller_done = 0;

    // make shared controller

    // start its processing
    auto pcontroller = std::shared_ptr<controller>{ new controller,
        [&](auto*p) {
            delete p;
            auto lock = std::unique_lock<std::mutex>(done_mutex);
            is_controller_done = true;
            std::cout << "controller destroyed" << std::endl;
            lock.unlock();
            controller_done.notify_all();
    }};
    pcontroller->start();

    // destroy the controlling pointer. but our controller is still running...
    pcontroller.reset();

    auto lock = std::unique_lock<std::mutex>(done_mutex);
    controller_done.wait(lock, [&]{ return is_controller_done;});
    std::cout << "program ends" << std::endl;
}

答案 3 :(得分:1)

enable_shared_from_thisshared_from_this的用例很明确,但我倾向于说在大多数用例中,可以将其删除,转而使用static方法获取{ {1}}然后从中创建一个新的shared_ptr(以与OP建议的方法非常相似的方式,但使用静态方法来支持创建新的shared_ptr)。 / p>

shared_ptr方法方法的优势是,当没有基础static时,您不会遇到尝试获取shared_from_this的错误对于此实例,由shared_ptr生成。

缺点是API隐式要求调用者带有bad_weak_ptr,因此如果调用者只有一个指向实例的原始指针,则他无法使用它(调用者可以从原始指针创建shared_ptr并调用该方法,但是如何判断原始指针是否未被shared_ptr管理?)。另一方面,如果用户手头有shared_ptr,他应该确定将其转为unique_ptr以便调用静态方法应该没问题。

在某种程度上,优点和缺点是同一枚硬币的两面。

在大多数情况下,我更希望API能够与shared_ptr一起使用(它在某种程度上取决于它),而不是允许使用任何类型的指针,希望有一个管理shared_ptr为此。这与使用不能以错误方式使用的API的建议相得益彰。

以下是@RichardHodges(很好的例子!)使用静态方法方法而不是shared_ptr提供的代码:

enable_shared_from_this

代码:http://coliru.stacked-crooked.com/a/281b0ef6d1b31c56