Let's say I have a class which is a child class of enable_shared_from_this. The documentation of this base class says there should be a shared pointer which owns this class before calling shared_from_this. Is it safe to allocate the class with new and call shared_from_this to manage the object?
No, it's not safe. You should only call shared_from_this
if the object is managed by a shared_ptr
, not allocated via new
(without an 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 this is OK:
std::shared_ptr<Test> test(new Test);
std::shared_ptr<Test> test2 = test->getptr();
getptr
because the latter will throw unless it is already managed (by a shared_ptr). –
Outfight As already mentioned by other users, calls to shared_from_this
on instances that are not owned by shared_ptr
s will result in an undefined behavior (usually an exception, but there are no guarantees).
So, why one more answer?
Because I did myself the same question once and got almost the same answer, then I started struggling with another question that immediately arose after that - how can I guarantee thus that all the instances are managed by a shared_ptr
?
For the sake of completeness, I add another answer with a few details about this aspect.
Here a simple solution that had not been mentioned before.
So simple a solution, indeed: private constructors, factory method and variadic templates.
It follows a snippet that mixes all of them 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 basic (trivial?) idea is to forbid the explicit construction of new instances, but by using the factory method here called create
.
Variadic templates are used to avoid writing several factory methods, nothing more. Perfect forwarding helps us to do that the right way.
Pretty simple, isn't it?
Anyway it took me a while to figure out that, so I hope this will help future readers once across the same doubt.
From the standard:
§ 20.8.2.4
shared_ptr shared_from_this();
shared_ptr shared_from_this() const;
7 *Requires: enable_shared_from_this shall be an accessible base class of T. this shall be a subobject of an object t of type T. There shall be at least one shared_ptr instance p that owns &t.
8 Returns: A shared_ptr object r that shares ownership with p.
9 Postconditions: r.get() == this
If you call shared_from_this()
within a class that is not managed by a shared_ptr
the result will be undefined behaviour because you have not fulfilled one of the documented preconditions of the method.
I know from experience that in [the current version of] libc++ the result is an exception being thrown. However, like all undefined behavior this must not be relied upon.
The documentation of this base class says there should be a shared pointer which owns this [object] before calling shared_from_this.
Okay, cool.
Is it safe to allocate the [object] with new and call shared_from_this to manage the object?
No. There should be a shared pointer which owns this [object] before calling shared_from_this.
No, it's not safe. You should only call shared_from_this
if the object is managed by a shared_ptr
, not allocated via new
(without an 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 this is OK:
std::shared_ptr<Test> test(new Test);
std::shared_ptr<Test> test2 = test->getptr();
getptr
because the latter will throw unless it is already managed (by a shared_ptr). –
Outfight This is not safe at all. Calling shared_from_this() from a non-shared_ptr invokes a bad_weak_ptr exception.
#include <iostream>
#include <memory>
struct A : std::enable_shared_from_this<A>
{
A(A *parent, int x): parent(parent), x(x) {
std::cout << "create A with " << x << std::endl;
}
void print(){
std::cout << "print A : " << x << std::endl;
}
~A(){
std::cout << "delete A" << std::endl;
}
A *parent;
int x;
};
void useA(const std::shared_ptr<A> &a) {
a->print();
a->parent->print();
for(auto parent = a->parent; parent; parent = parent->parent){
auto aptr = parent->shared_from_this();
aptr->print();
}
}
int main() {
auto a1 = new A(NULL, 0);
auto p1 = std::make_shared<A>(a1, 1);
std::cout << "main" << std::endl;
useA(p1);
}
In this example, std::bad_weak_ptr is thrown at
auto aptr = parent->shared_from_this();
© 2022 - 2025 — McMap. All rights reserved.