Is there a way to prevent shared_from_this() call for a stack-allocated object ?
The enable_shared_from_this<> in the base classes list is a 开发者_StackOverflowstrong indicator for class user, but is there a way to enforce the correct usage ?Example code:
class C : public enable_shared_from_this<C>
{
public:
shared_ptr<C> method() { return shared_from_this(); }
};
void func()
{
C c;
shared_ptr<C> ptr = c.method(); // exception coming from shared_from_this()
}
So to protect against this problem you can make your constructors private and only provide creation functions that return shared_ptr - this way the object can't be allocated on the stack, like this:
class C : public enable_shared_from_this<C>
{
public:
static shared_ptr<C> create() { return shared_ptr<C>(new C() ); }
shared_ptr<C> method() { shared_from_this(); }
private:
C() {...}
// Make operator= and C(const C&) private unimplemented
// so the used cant do bad things like C c( * c_ptr );
C& operator=( const C & );
C( const C & );
};
void func()
{
C c; // This doesn't compile
shared_ptr<C> ptr = c.method(); // So you can never get this
}
void altfunc()
{
shared_ptr<C> c_ptr = C::create();
C & c_ref = *c;
shared_ptr<C> ptr = c_ref.method(); // OK
}
If you find yourself wishing for an operator=
you can provide a clone function using a private implemented copy constructor, something like this
// This goes in class C
shared_ptr<C> C::clone() const
{
return shared_ptr<C>( new C(*this) );
}
// This is how you can use it
shared_ptr<C> c2 = c1->clone();
I found the solution.
In Dune library they use stack-compatible enable_shared_from_this
class template adaptation.
Check the original source code here or check my example with the quick copy&paste & cleanup of code:
#include <cstdio>
#include <cassert>
#include <memory>
#include <iostream>
template<class T>
struct null_deleter
{
void operator() (T*) const {}
};
template<typename T>
inline std::shared_ptr<T> stackobject_to_shared_ptr(T & t)
{
return std::shared_ptr<T>(&t, null_deleter<T>());
}
template<typename T, typename T2>
inline std::shared_ptr<T2> stackobject_to_shared_ptr(T & t)
{
return std::shared_ptr<T2>(dynamic_cast<T2*>(&t), null_deleter<T2>());
}
template<typename T>
class stack_compatible_enable_shared_from_this
: public std::enable_shared_from_this<T>
{
public:
std::shared_ptr<T> shared_from_this()
{
try
{
return std::enable_shared_from_this<T>::shared_from_this();
}
catch (std::bad_weak_ptr&)
{
_local_ptr = stackobject_to_shared_ptr(*static_cast<T*>(this));
return _local_ptr;
}
}
std::shared_ptr<const T> shared_from_this() const
{
try
{
return std::enable_shared_from_this<T>::shared_from_this();
}
catch (std::bad_weak_ptr&)
{
_local_ptr = stackobject_to_shared_ptr(*const_cast<T*>(static_cast<const T*>(this)));
return _local_ptr;
}
}
private:
mutable std::shared_ptr<T> _local_ptr;
};
struct MyObj : public stack_compatible_enable_shared_from_this<MyObj>{};
int main (int argc, char **argv) {
//std::shared_ptr<MyObj> so = std::make_shared<MyObj>(6);
MyObj o{};
auto * so = &o;
{
auto l = std::weak_ptr<MyObj>(so->shared_from_this());
auto shared = l.lock();
if (shared) { } //use it
}
}
精彩评论