Using shared_from_this () without a managed shared pointer in C ++ 11

Let's say I have a class that is a child of enable_shared_from_this. The documentation for this base class says that before calling shared_from_this there must be a common pointer to which this class belongs. Is it safe to assign a class to new ones and call shared_from_this to control the object?

+5
source share
4 answers

No, this is not safe. You should only call shared_from_this if the object is managed by shared_ptr and not allocated through new (without the associated shared_ptr ). For example, this code

 struct Test: std::enable_shared_from_this<Test> { std::shared_ptr<Test> getptr() { return shared_from_this(); } }; Test *test = new Test; std::shared_ptr<Test> test2 = test->getptr(); 

will throw std::bad_weak_ptr (at least when using libstdc++ ). But it normal:

 std::shared_ptr<Test> test(new Test); std::shared_ptr<Test> test2 = test->getptr(); 
+1
source

From the standard:

ยง 20.8.2.4

shared_ptr shared_from_this ();

shared_ptr shared_from_this () const;

7 * Required: enable_shared_from_this must be an accessible base class of T. , this will be a subobject of an object t of type T. There must be at least one instance of shared_ptr p to which & t belongs.

8 Returns: the shared_ptr r object that owns p.

9 Postconditions: r.get () == this

If you call shared_from_this() inside a class that is not controlled by shared_ptr , the result will be undefined behavior because you did not fulfill one of the documented prerequisites of the method.

I know from experience that in the [current version] libC ++, the result is an exception. However, like all undefined behavior, this cannot be relied on.

+3
source

The documentation for this base class says that before calling shared_from_this there must be a common pointer to which this [object] belongs.

Nice cool.

Is it possible to select [object] new and call shared_from_this to control the object?

No. Before calling shared_from_this, there must be a common pointer to which this [object] belongs.

+2
source

As mentioned by other users, calls to shared_from_this for instances not owned by shared_ptr will lead to undefined behavior (this is usually an exception, but there are no guarantees).

So why another answer?

Since I asked the same question myself and got almost the same answer, then I began to struggle with another question that arose immediately after that - how can I guarantee that all instances are managed using shared_ptr ?

For completeness, I will add another answer with a few details about this aspect.
Here's a simple solution that was not mentioned before.

So a simple solution, really: private constructors, factory method and variable templates.
It follows a snippet that mixes them all together in a minimal example:

 #include<memory> #include<utility> class C: public std::enable_shared_from_this<C> { C() = default; C(const C &) = default; C(C &&) = default; C& operator=(const C &) = default; C& operator=(C &&c) = default; public: template<typename... Args> static std::shared_ptr<C> create(Args&&... args) noexcept { return std::shared_ptr<C>{new C{std::forward<Args>(args)...}}; } std::shared_ptr<C> ptr() noexcept { return shared_from_this(); } }; int main() { std::shared_ptr<C> c1 = C::create(); std::shared_ptr<C> c2 = C::create(*c1); std::shared_ptr<C> c3 = c2->ptr(); // these won't work anymore... // C c4{}; // std::shared_ptr<C> c5 = std::make_shared<C>(); // std::shared_ptr<C> c6{new C{}}; // C c7{*c1}; // ... and so on ... } 

The main (trivial?) Idea is to prohibit the explicit construction of new instances, but using the factory method called create .
Variadic templates are used to avoid writing multiple factory methods, nothing more. Perfect shipment helps us make the right way.

Pretty simple, right? In any case, it took me a while to figure this out, so I hope this helps future readers develop the same doubts.

+1
source

Source: https://habr.com/ru/post/1237888/


All Articles