2016-07-07 87 views
2

为什么我应该使用enable_shared_from_this,因为我可以通过普通赋值获得相同的效果。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; 
} 
+0

'空A :: member_function(){some_other_function (shared_from_this()); }' –

回答

4

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

有一个在发布代码的方法没有什么区别,正是因为从std::enable_shared_from_this<A>A继承。这两种p1和p2是引用同一具体对象的shared_ptr对象(假设只有其中一个部分为您的测试编译,否则您在id名称重用时出错)。由T类型的一些预先存在的std::shared_ptr<T>或其衍生物管理,在哟你没有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>作为参数。如果没有std::enable_shared_from_this<A>作为基础,则从A::method()的主体中不存在这样的机制。没有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); 
} 

这显然是可怕的和可怕的。此外,不保证memethod实际上是thisstd::shared_ptr<T>。因此标准委员会用std::enable_shared_from_this<T>祝福了我们。

+0

感谢您的解释。 –

+0

自动变量不是'shared_ptr'管理的。 FTFY。而不是downvoting和解释。 :) –

+0

@ Cheersandhth.-Alf非常感谢你。我完全从该样本的先前版本中复制/粘贴了该文件。先生,你是最棒的。再次感谢。 – WhozCraig

1

共享从 - 该功能可以以获得shared_ptrshared_ptr -managed对象时你已经是一个原始指针或参考。

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

+0

我知道,在上面的例子中,我没有创建原始指针。它使用make_shared <>。 –

1

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

最常见的用例是在一些异步进程正在运行时“让自己活着”。一个很好的例子是一个完成处理程序,另一个将在'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; 
} 
1

enable_shared_from_thisshared_from_this使用情况是清楚的,但我倾向于说,在大多数使用情况下,它可以赞成static方法,可推动投下了shared_ptr和然后从中创建一个新的shared_ptr(与OP建议的方法非常类似,但是使用静态方法来支持创建新的shared_ptr)。

static方法方法的优点是,你不会落于试图让shared_from_this当有此实例没有基础shared_ptr,与bad_weak_ptr导致的错误。

缺点是空气污染指数是隐含询问主叫方拿出一个shared_ptr,所以如果主叫方刚刚原始指针的情况下,他不能使用它(调用者可以创建从一个shared_ptr原始指针并调用该方法,但他怎么知道原始的原始指针是否已被shared_ptr管理?)。另一方面,如果用户手头有一个unique_ptr,他应该确定将其转换为shared_ptr以调用静态方法应该没问题。

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

我希望在大多数情况下,要求API与shared_ptr(它已经依赖于某种方式)一起工作,而不是允许使用任何类型的指针,希望有一个管理的shared_ptr它。这与使用不易以错误方式使用的API的建议相符。

这里是@RichardHodges给出的代码使用静态方法的方法,而不是使用enable_shared_from_this(很好的例子!):

// code based on Richard Hodges example 

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(); 
} 

// without the need to inherit from std::enable_shared_from_this 
struct Controller { 
    auto get_lock() const { 
     return std::unique_lock<std::mutex>(_mutex); 
    } 
    static void start(std::shared_ptr<Controller>& pcontroller) { 
     long_process_with_completion_handler(
     [self = std::shared_ptr<Controller>(pcontroller)] { 
      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 and 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(); 
    }}; 
    Controller::start(pcontroller); 

    // 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; 
} 

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