I love Boost's smart_ptr features and the ability to convert to and from a shared_ptr and weak_ptr, but since the reference count is not contained in the pointed class itself, the following code does not work (and it shouldn't).
A *a = new A;
shared_ptr<A> aPtr1(a);
{
shared_ptr<A> aPtr2(a);
// The reference counts of aPtr1 and aPtr2 are both 1.
} // At this point, `a` is destructed by aPtr2.
aPtr1->foo(); // And... SIGTERM
I believe the JUCE framework has this functionality. [ReferenceCountedObject and ReferenceCountedObjectPtr]
However, I'd rather use Boost for my application. Is it possible to allow Boost smart_ptrs to look for the reference count in the pointed class rather than the private boost::detail::shared_count instance?
Simple solution:
A *a = new A;
shared_ptr<A> aPtr1(a);
{
// construct new shared pointer from old one.
shared_ptr<A> aPtr2(aPtr1);
}
aPtr1->foo();
If you want something more complicated, see http://www.boost.org/libs/smart_ptr/enable_shared_from_this.html :
The header <boost/enable_shared_from_this.hpp> defines the class template enable_shared_from_this. It is used as a base class that allows a shared_ptr to the current object to be obtained from within a member function.
Edit: I should have mentioned that enable_shared_from_this has some unfortunate issues with derivation. However, the following works with c++11; I didn't try it with boost but I suppose it should work there, too. I think it's a bit of a hack; using raw pointers when you use shared_ptr's is bound to end in tears:
struct X : std::enable_shared_from_this {
/* stuff */
};
struct Y : X {
/* stuff */
void yonly() {};
};
int main() {
Y* y = new Y;
shared_ptr<Y> sy(y);
sy->yonly();
{
auto sy2 = std::shared_ptr<Y>(y->shared_from_this(), y);
sy2->yonly();
}
std::cout << "Block ended" << std::endl;
return 0;
}
boost::intrusive_ptr likely fits your requirements.
To note however, with shared_ptr, you should construct them as follows:
shared_ptr<A> aPtr1 = boost::make_shared<A>();
This is not exception safe:
// call this as in shared_ptr<T> foo = create_intrusive_shared( new T(blah) );
// This takes ownership of the pointer you pass it.
template<typename T>
std::shared_ptr<T> create_intrusive_shared( T* t )
{
auto retval = std::shared_ptr<T>( t, []( T* cleanup )
{
if (cleanup)
cleanup->RemoveRef();
});
return retval;
}
// Call this if you have an existing instance of T, whose ownership is being
// maintained elsewhere. Do not call it with new T() as an argument, unless
// new instances of T are created with a 0 ref count
template<typename T>
std::shared_ptr<T> make_intrusive_shared( T* t )
{
if (t)
t->AddRef();
auto retval = create_intrusive_shared(t);
return retval;
}
Making them exception safe takes a bit more work. You will want to reimplement make_shared, but tag the resulting shared_ptr with a cleanup function.
Related
In the following code example:
#include <iostream>
class Foo{
};
class Bar{
public:
void addFoo(Foo *foo){
auto my_foo = std::shared_ptr<Foo>(foo);
}
};
int main() {
auto bar = Bar();
bar.addFoo(new Foo());
return 0;
}
Do I need to clean up the pointer created in main() by the bar.addFoo(new Foo) call, or will this be taken care of by Bar which creates a shared_ptr of it? My understanding is that auto my_foo = std::shared_ptr<Foo>(foo); will use the copy constructer to copy this pointer into my_foo leaving the original one dangling, is that correct?
The very idea of a constructor taking a raw pointer is to pass the ownership to std::shared_ptr. So, no, you don't have to delete a raw pointer passed to std::shared_ptr. Doing this will lead to a double deletions, which is UB.
Note that in general passing a raw pointer is dangerous. Consider the following more generalized example:
void addFoo(Foo *foo){
// some code which could throw an exception
auto my_foo = std::shared_ptr<Foo>(foo);
}
If an exception is throw before my_foo is constructed, foo will leak.
If you have no special reason to pass a raw pointer, consider the following alternative:
class Bar {
public:
template<class... Args>
void addFoo(Args... args){
auto my_foo = std::make_shared<Foo>(args...);
}
};
int main() {
auto bar = Bar();
bar.addFoo();
return 0;
}
Here you pass arguments (if you have any) to construct Foo inside addFoo() instead of constructing Foo before invoking addFoo().
Perfect forwarding of args... could be used if it is needed:
template<class... Args>
void addFoo(Args&&... args){
auto my_foo = std::make_shared<Foo>(std::forward<Args>(args)...);
}
The code you wrote is correct. But in modern C++, you should not be using raw pointers, new and delete unless you have to interoperate with code that does. If you can help it (and if question comments are any indication, you can), use smart pointers all the way through:
#include <iostream>
#include <memory>
class Foo {};
class Bar {
public:
void addFoo(std::unique_ptr<Foo> foo) {
auto my_foo = std::shared_ptr<Foo>(std::move(foo));
}
};
int main() {
auto bar = Bar();
bar.addFoo(std::make_unique<Foo>());
return 0;
}
Above, the addFoo member function receives the pointer as a unique_ptr, and uses std::move to transfer ownership of the pointer from the unique_ptr to the shared_ptr without copying the referent; after constructing the shared_ptr, the unique_ptr is left in an empty state. You could also have addFoo receive a shared_ptr directly, or construct the object in-place inside the member function, as in Evg’s answer.
Using unique_ptr instead of a raw pointer makes it clear that the method intends to take ownership of the allocation, and encourages callers to use smart pointers themselves, making it less likely they will forget to delete their allocations later.
A raw pointer does not manage end of life, but a shared pointer does. When you create a shared pointer from a raw pointer, the shared pointer takes ownership of the object. That means that the object will be destroyed when the last shared pointer pointing to it will go out of scope.
In your code, my_foo takes ownership of the object created with new Foo(), goes out of scope when addFoo returns, and as it contains the only shared reference, correctly destroys the object.
The correct, c++ way to do this, would be the following:
#include <iostream>
class Foo{
};
class Bar{
public:
void addFoo(Foo foo){
auto my_foo = std::make_shared<Foo>(foo);
}
};
int main() {
auto bar = Bar();
bar.addFoo(Foo());
return 0;
}
This avoids any raw pointers or naked new, and is totally exception safe. Also, std::make_shared introduces some performance benefits.
One confusing thing here is that the code seems to be unnecessarily copy the Foo object, however, since C++17, due to Return Value Optimization, (RVO), you are guaranteed to have no copies at all (when passing Foo as an argument to addFoo).
You can create the shared pointer with make_shared. If you want to construct Foo in main (e.g. because you have the paramters available there), then use make_shared at the point of construction and pass the shared_ptr on.
#include <iostream>
class Foo{
~Foo() { std::cout << "Foo destructed" << std::endl; }
};
class Bar{
public:
void addFoo(std::shared_ptr<Foo> foo){
auto my_foo = foo;
}
};
int main() {
auto bar = Bar();
bar.addFoo(std::make_shared<Foo>());
return 0;
}
delete also calls your destructor. You can test, whether the shared pointer destructs your object or whether a delete is needed by printing out a message.
I ran across enable_shared_from_this while reading the Boost.Asio examples and after reading the documentation I am still lost for how this should correctly be used. Can someone please give me an example and explanation of when using this class makes sense.
It enables you to get a valid shared_ptr instance to this, when all you have is this. Without it, you would have no way of getting a shared_ptr to this, unless you already had one as a member. This example from the boost documentation for enable_shared_from_this:
class Y: public enable_shared_from_this<Y>
{
public:
shared_ptr<Y> f()
{
return shared_from_this();
}
}
int main()
{
shared_ptr<Y> p(new Y);
shared_ptr<Y> q = p->f();
assert(p == q);
assert(!(p < q || q < p)); // p and q must share ownership
}
The method f() returns a valid shared_ptr, even though it had no member instance. Note that you cannot simply do this:
class Y: public enable_shared_from_this<Y>
{
public:
shared_ptr<Y> f()
{
return shared_ptr<Y>(this);
}
}
The shared pointer that this returned will have a different reference count from the "proper" one, and one of them will end up losing and holding a dangling reference when the object is deleted.
enable_shared_from_this has become part of C++ 11 standard. You can also get it from there as well as from boost.
from Dr Dobbs article on weak pointers, I think this example is easier to understand (source: http://drdobbs.com/cpp/184402026):
...code like this won't work correctly:
int *ip = new int;
shared_ptr<int> sp1(ip);
shared_ptr<int> sp2(ip);
Neither of the two shared_ptr objects knows about the other, so both will try to release the resource when they are destroyed. That usually leads to problems.
Similarly, if a member function needs a shared_ptr object that owns the object that it's being called on, it can't just create an object on the fly:
struct S
{
shared_ptr<S> dangerous()
{
return shared_ptr<S>(this); // don't do this!
}
};
int main()
{
shared_ptr<S> sp1(new S);
shared_ptr<S> sp2 = sp1->dangerous();
return 0;
}
This code has the same problem as the earlier example, although in a more subtle form. When it is constructed, the shared_ptr object sp1 owns the newly allocated resource. The code inside the member function S::dangerous doesn't know about that shared_ptr object, so the shared_ptr object that it returns is distinct from sp1. Copying the new shared_ptr object to sp2 doesn't help; when sp2 goes out of scope, it will release the resource, and when sp1 goes out of scope, it will release the resource again.
The way to avoid this problem is to use the class template enable_shared_from_this. The template takes one template type argument, which is the name of the class that defines the managed resource. That class must, in turn, be derived publicly from the template; like this:
struct S : enable_shared_from_this<S>
{
shared_ptr<S> not_dangerous()
{
return shared_from_this();
}
};
int main()
{
shared_ptr<S> sp1(new S);
shared_ptr<S> sp2 = sp1->not_dangerous();
return 0;
}
When you do this, keep in mind that the object on which you call shared_from_this must be owned by a shared_ptr object. This won't work:
int main()
{
S *p = new S;
shared_ptr<S> sp2 = p->not_dangerous(); // don't do this
}
Here's my explanation, from a nuts and bolts perspective (top answer didn't 'click' with me). *Note that this is the result of investigating the source for shared_ptr and enable_shared_from_this that comes with Visual Studio 2012. Perhaps other compilers implement enable_shared_from_this differently...*
enable_shared_from_this<T> adds a private weak_ptr<T> instance to T which holds the 'one true reference count' for the instance of T.
So, when you first create a shared_ptr<T> onto a new T*, that T*'s internal weak_ptr gets initialized with a refcount of 1. The new shared_ptr basically backs onto this weak_ptr.
T can then, in its methods, call shared_from_this to obtain an instance of shared_ptr<T> that backs onto the same internally stored reference count. This way, you always have one place where T*'s ref-count is stored rather than having multiple shared_ptr instances that don't know about each other, and each think they are the shared_ptr that is in charge of ref-counting T and deleting it when their ref-count reaches zero.
There is one particular case where I find enable_shared_from_this extremely useful: Thread safety when using asynchronous callback.
Imagine class Client has a member of type AsynchronousPeriodicTimer:
struct AsynchronousPeriodicTimer
{
// call this periodically on some thread...
void SetCallback(std::function<void(void)> callback);
void ClearCallback(); // clears the callback
}
struct Client
{
Client(std::shared_ptr< AsynchronousPeriodicTimer> timer)
: _timer(timer)
{
_timer->SetCallback(
[this]
()
{
assert(this); // what if 'this' is already dead because ~Client() has been called?
std::cout << ++_counter << '\n';
}
);
}
~Client()
{
// clearing the callback is not in sync with the timer, and can actually occur while the callback code is running
_timer->ClearCallback();
}
int _counter = 0;
std::shared_ptr< AsynchronousPeriodicTimer> _timer;
}
int main()
{
auto timer = std::make_shared<AsynchronousPeriodicTimer>();
{
auto client = std::make_shared<Client>(timer);
// .. some code
// client dies here, there is a race between the client callback and the client destructor
}
}
The client class subscribes a callback function to the periodic timer. Once the client object goes out of scope, there is a race condition between the client's callback and the client's destructor. The callback may be invoked with a dangling pointer!
The solution: using enable_shared_from_this to extend the object lifetime for the duration of the callback invocation.
struct Client : std::enable_shared_from_this<Client>
{
Client(std::shared_ptr< AsynchronousPeriodicTimer> timer)
: _timer(timer)
{
}
void Init()
{
auto captured_self = weak_from_this(); // weak_ptr to avoid cyclic references with shared_ptr
_timer->SetCallback(
[captured_self]
()
{
if (auto self = captured_self.lock())
{
// 'this' is guaranteed to be non-nullptr. we managed to promote captured_self to a shared_ptr
std::cout << ++self->_counter << '\n';
}
}
);
}
~Client()
{
// the destructor cannot be called while the callback is running. shared_ptr guarantees this
_timer->ClearCallback();
}
int _counter = 0;
std::shared_ptr< AsynchronousPeriodicTimer> _timer;
}
The mechanism of enable_shared_from_this, combined with the inherent thread safety of std::shared_ptr reference counting, enable us to ensure that the Client object cannot be destructed while the callback code is accessing its internal members.
Note that the Init method is separated from the constructor since the initialization process of enable_shared_from_this is not finalized until the constructor exits. Hence the extra method. It is generally unsafe to subscribe an asynchronous callback from within a constructor since the callback may access uninitialized fields.
Note that using a boost::intrusive_ptr does not suffer from this problem.
This is often a more convenient way to get around this issue.
It's exactly the same in c++11 and later: It is to enable the ability to return this as a shared pointer since this gives you a raw pointer.
in other word, it allows you to turn code like this
class Node {
public:
Node* getParent const() {
if (m_parent) {
return m_parent;
} else {
return this;
}
}
private:
Node * m_parent = nullptr;
};
into this:
class Node : std::enable_shared_from_this<Node> {
public:
std::shared_ptr<Node> getParent const() {
std::shared_ptr<Node> parent = m_parent.lock();
if (parent) {
return parent;
} else {
return shared_from_this();
}
}
private:
std::weak_ptr<Node> m_parent;
};
I'm wondering when we should use unique_ptr (or boost::scope_ptr) instead of local object.
There are two possible cases I can think of:
The object is large that exceed stack size of a thread. But in this case you can always increase size of thread.
Polymorphism. e.g. unique_ptr<C> p; if ... p.reset(new C1); else p.reset(new C2);. But I am not sure when exactly we need this. If this p is a parameter for a function, we can simply say: if ... foo(new C1); else foo(new C2);
Are there any other cases that we should use unique_ptr instead of local object?
Polymorphism
Polymorphism is a common reason. A typical example is your object is created by a factory that returns a unique_ptr:
std::unique_ptr<C> factoryFunction(int arg) {
switch (arg) {
case 1:
return std::make_unique<C1>();
case 2:
return std::make_unique<C2>();
default:
return nullptr;
}
}
void someFunction(int arg) {
auto c = factoryFunction(arg);
if (c) {
// do something with c...
}
}
Transfer ownership
In your comment you say you prefer shared_ptr if you need a variable that lives longer than the scope it is declared. I think you should actually prefer unique_ptr. By returning a unique_ptr you are transferring ownership to the caller. Like factoryFunction does above. Or perhaps to return a big expensive-to-move object:
using BigArray = std::array<BigPOD, 1000>;
std::unique_ptr<BigArray> getBig() {
auto big = std::make_unique<BigArray>();
// fill big...
return big;
}
unique_ptr has less overhead than shared_ptr and it makes ownership clearer. I would only use shared_ptr if ownership needs to be shared.
Passing a unique_ptr into a function means you are transferring ownership into the function (a "sink"). For example a constructor:
class Foo {
private:
std::unique_ptr<BigArray> array_;
public:
Foo(std::unique_ptr<BigArray> array) : array_(std::move(array)) {}
};
void someFunction() {
auto big = getBig();
auto foo = Foo(std::move(big));
// do something with foo...
}
I have a question similar to How to manage object life time using Boost library smart pointers? but, in my case, the "object" isn't a C++ object at all, but an opaque type returned/passed out from a C API. The type does not have pointer semantics, i.e., there is no dereferencing; it is, however, passed as an argument to other functions in the C API. The type also has a definitive close API which must be called in order to clean up internal resources.
So, I have a C API that's something along the lines of
opaque_legacy_type_t x;
XXopen(..., &x); // allocates/opens resource and fills out 'x' to be used later
XXdoSomethingWithResource(x, ...); // do something with resources related to 'x'
...more actions...
XXclose(x); // closes and cleans up resources related to 'x'
For various reasons, in my C++ code I would like to manage "instances" of opaque_legacy_type_t much like I would manage heap-allocated object instances, i.e. with similar sharing semantics as boost::shared_ptr<>. It seems that shared_ptr offers enough that I can manage calling XXclose by doing this:
opaque_legacy_type_t x;
XXopen(..., &x);
boost::shared_ptr<opaque_legacy_type_t> managed(x, XXclose);
But, since opaque_legacy_type_t doesn't have pointer semantics, the usage of managed is a bit clumsy.
What I'd like to do is have something like a managed_type that is similar to shared_ptr, and am looking for ideas that don't require me to write it all.
EDIT: I corrected my original screw-up in the example. The legacy API takes the opaque type by value rather than by pointer.
Since all of the legacy API take a pointer to the opaque type, you could use shared pointers directly. The key is for you to not declare the original structure on the stack, but rather allocate it via new:
int main () {
std::shared_ptr<opaque_legacy_type_t> x(new opaque_legacy_type_t,
[](opaqeue_legacy_type_t* p) { XXClose(p); delete p; });
XXopen(..., x.get());
XXdoSomethingWithResource(x.get(), ...);
}
EDIT: If some API take the opaque type by value instead of pointer, then pass the dereferenced pointer.
int main () {
std::shared_ptr<opaque_legacy_type_t> x(new opaque_legacy_type_t,
[](opaqeue_legacy_type_t* p) { XXClose(*p); delete p; });
XXopen(..., x.get());
XXdoSomethingWithResource(*x, ...);
}
You could use boost smart pointers together with the pimpl idom:
class shared_opaque_legacy_type_t {
struct impl {
opaque_legacy_type_t t;
impl(...) { XXOpen(..., t); }
~impl(...) { XXClose(t); }
}
boost::shared_ptr<impl> _impl;
public:
shared_opaque_lagacy_type_t(...) : _impl(new impl(...)) {}
opaque_legacy_type_t* get() {
return _impl->t;
}
};
shared_opaque_legacy_type_t x(...);
XXdoSomethingWithResource(x.get(), ...);
The drawback is that you could still call XXclose(x.get()) and invalidate your object.
UPDATE: Fixed it. :-)
You could write a wrapper to use with boost that will call the open() in the ctor and the close() in the dtor.
I voted for Rob's answer that just uses a shared_ptr with no wrapper, but if you really want to avoid dynamic allocation here's a simple little example of how to do that.
It's a template that directly holds the handle and does no allocation. You pass the constructor a functor that creates an object of the opaque type, and a deleter to call when the type needs to be destroyed. It's movable and non-copyable so now shared reference count is needed. It implements implicit conversion operators so you can use it where you'd use a value of the held type.
template<typename T,typename D>
class opaque_type_handle {
T handle;
D deleter;
bool needs_delete;
public:
template<typename F>
opaque_type_handle(F f,D d) : handle(f()), deleter(d), needs_delete(true) {}
opaque_type_handle(opaque_type_handle const &) = delete;
opaque_type_handle &operator=(opaque_type_handle const &) = delete;
opaque_type_handle(opaque_type_handle &&rhs) : handle(rhs.handle),deleter(rhs.deleter),needs_delete(true) {
rhs.needs_delete = false;
}
opaque_type_handle &operator=(opaque_type_handle &&rhs) {
handle = rhs.handle;
deleter = rhs.deleter;
needs_delete = true;
rhs.needs_delete = false;
returh *this;
}
~opaque_type_handle() {
if(needs_delete) {
deleter(handle);
}
}
operator T&() { return handle; }
operator T() const { return handle; }
};
Use it like so:
// wrap up the code for creating an opaque_legacy_type_t handle
typedef opaque_type_handle<opaque_legacy_type_t,decltype(&XXclose)> legacy_handle;
legacy_handle make_legacy_handle(...) {
return legacy_handle(
[](){
opaque_legacy_type_t tmp;
XXopen(..., &tmp);
return tmp;
},
&XXclose
);
}
legacy_handle x = make_legacy_handle(...);
XXdoSomethingWithResource(x,...);
I ran across enable_shared_from_this while reading the Boost.Asio examples and after reading the documentation I am still lost for how this should correctly be used. Can someone please give me an example and explanation of when using this class makes sense.
It enables you to get a valid shared_ptr instance to this, when all you have is this. Without it, you would have no way of getting a shared_ptr to this, unless you already had one as a member. This example from the boost documentation for enable_shared_from_this:
class Y: public enable_shared_from_this<Y>
{
public:
shared_ptr<Y> f()
{
return shared_from_this();
}
}
int main()
{
shared_ptr<Y> p(new Y);
shared_ptr<Y> q = p->f();
assert(p == q);
assert(!(p < q || q < p)); // p and q must share ownership
}
The method f() returns a valid shared_ptr, even though it had no member instance. Note that you cannot simply do this:
class Y: public enable_shared_from_this<Y>
{
public:
shared_ptr<Y> f()
{
return shared_ptr<Y>(this);
}
}
The shared pointer that this returned will have a different reference count from the "proper" one, and one of them will end up losing and holding a dangling reference when the object is deleted.
enable_shared_from_this has become part of C++ 11 standard. You can also get it from there as well as from boost.
from Dr Dobbs article on weak pointers, I think this example is easier to understand (source: http://drdobbs.com/cpp/184402026):
...code like this won't work correctly:
int *ip = new int;
shared_ptr<int> sp1(ip);
shared_ptr<int> sp2(ip);
Neither of the two shared_ptr objects knows about the other, so both will try to release the resource when they are destroyed. That usually leads to problems.
Similarly, if a member function needs a shared_ptr object that owns the object that it's being called on, it can't just create an object on the fly:
struct S
{
shared_ptr<S> dangerous()
{
return shared_ptr<S>(this); // don't do this!
}
};
int main()
{
shared_ptr<S> sp1(new S);
shared_ptr<S> sp2 = sp1->dangerous();
return 0;
}
This code has the same problem as the earlier example, although in a more subtle form. When it is constructed, the shared_ptr object sp1 owns the newly allocated resource. The code inside the member function S::dangerous doesn't know about that shared_ptr object, so the shared_ptr object that it returns is distinct from sp1. Copying the new shared_ptr object to sp2 doesn't help; when sp2 goes out of scope, it will release the resource, and when sp1 goes out of scope, it will release the resource again.
The way to avoid this problem is to use the class template enable_shared_from_this. The template takes one template type argument, which is the name of the class that defines the managed resource. That class must, in turn, be derived publicly from the template; like this:
struct S : enable_shared_from_this<S>
{
shared_ptr<S> not_dangerous()
{
return shared_from_this();
}
};
int main()
{
shared_ptr<S> sp1(new S);
shared_ptr<S> sp2 = sp1->not_dangerous();
return 0;
}
When you do this, keep in mind that the object on which you call shared_from_this must be owned by a shared_ptr object. This won't work:
int main()
{
S *p = new S;
shared_ptr<S> sp2 = p->not_dangerous(); // don't do this
}
Here's my explanation, from a nuts and bolts perspective (top answer didn't 'click' with me). *Note that this is the result of investigating the source for shared_ptr and enable_shared_from_this that comes with Visual Studio 2012. Perhaps other compilers implement enable_shared_from_this differently...*
enable_shared_from_this<T> adds a private weak_ptr<T> instance to T which holds the 'one true reference count' for the instance of T.
So, when you first create a shared_ptr<T> onto a new T*, that T*'s internal weak_ptr gets initialized with a refcount of 1. The new shared_ptr basically backs onto this weak_ptr.
T can then, in its methods, call shared_from_this to obtain an instance of shared_ptr<T> that backs onto the same internally stored reference count. This way, you always have one place where T*'s ref-count is stored rather than having multiple shared_ptr instances that don't know about each other, and each think they are the shared_ptr that is in charge of ref-counting T and deleting it when their ref-count reaches zero.
There is one particular case where I find enable_shared_from_this extremely useful: Thread safety when using asynchronous callback.
Imagine class Client has a member of type AsynchronousPeriodicTimer:
struct AsynchronousPeriodicTimer
{
// call this periodically on some thread...
void SetCallback(std::function<void(void)> callback);
void ClearCallback(); // clears the callback
}
struct Client
{
Client(std::shared_ptr< AsynchronousPeriodicTimer> timer)
: _timer(timer)
{
_timer->SetCallback(
[this]
()
{
assert(this); // what if 'this' is already dead because ~Client() has been called?
std::cout << ++_counter << '\n';
}
);
}
~Client()
{
// clearing the callback is not in sync with the timer, and can actually occur while the callback code is running
_timer->ClearCallback();
}
int _counter = 0;
std::shared_ptr< AsynchronousPeriodicTimer> _timer;
}
int main()
{
auto timer = std::make_shared<AsynchronousPeriodicTimer>();
{
auto client = std::make_shared<Client>(timer);
// .. some code
// client dies here, there is a race between the client callback and the client destructor
}
}
The client class subscribes a callback function to the periodic timer. Once the client object goes out of scope, there is a race condition between the client's callback and the client's destructor. The callback may be invoked with a dangling pointer!
The solution: using enable_shared_from_this to extend the object lifetime for the duration of the callback invocation.
struct Client : std::enable_shared_from_this<Client>
{
Client(std::shared_ptr< AsynchronousPeriodicTimer> timer)
: _timer(timer)
{
}
void Init()
{
auto captured_self = weak_from_this(); // weak_ptr to avoid cyclic references with shared_ptr
_timer->SetCallback(
[captured_self]
()
{
if (auto self = captured_self.lock())
{
// 'this' is guaranteed to be non-nullptr. we managed to promote captured_self to a shared_ptr
std::cout << ++self->_counter << '\n';
}
}
);
}
~Client()
{
// the destructor cannot be called while the callback is running. shared_ptr guarantees this
_timer->ClearCallback();
}
int _counter = 0;
std::shared_ptr< AsynchronousPeriodicTimer> _timer;
}
The mechanism of enable_shared_from_this, combined with the inherent thread safety of std::shared_ptr reference counting, enable us to ensure that the Client object cannot be destructed while the callback code is accessing its internal members.
Note that the Init method is separated from the constructor since the initialization process of enable_shared_from_this is not finalized until the constructor exits. Hence the extra method. It is generally unsafe to subscribe an asynchronous callback from within a constructor since the callback may access uninitialized fields.
Note that using a boost::intrusive_ptr does not suffer from this problem.
This is often a more convenient way to get around this issue.
It's exactly the same in c++11 and later: It is to enable the ability to return this as a shared pointer since this gives you a raw pointer.
in other word, it allows you to turn code like this
class Node {
public:
Node* getParent const() {
if (m_parent) {
return m_parent;
} else {
return this;
}
}
private:
Node * m_parent = nullptr;
};
into this:
class Node : std::enable_shared_from_this<Node> {
public:
std::shared_ptr<Node> getParent const() {
std::shared_ptr<Node> parent = m_parent.lock();
if (parent) {
return parent;
} else {
return shared_from_this();
}
}
private:
std::weak_ptr<Node> m_parent;
};