I have a shared pointer with a custom deleter and want to create another shared pointer from it with a custom deleter that does some work before invoking the original custom deleter. Something like...
std::shared_ptr<X> foo;
{
std::shared_ptr<X> bar(new X(), Deleter);
foo.reset(bar, My_Deleter(bar.get_deleter()));
}
When bar goes out of scope, nothing should happen because of foo. When foo goes out of scope, My_Deleter runs and then invokes Deleter.
Is it possible to chain custom deleters like this? My_Deleter stores a copy of Deleter on construction, but I'm not sure what the type declaration would look like.
You have to specify the deleter type when calling shared_ptr::get_deleter(), so you have to specialize your custom delete class accordingly.
And I'm not sure you can pass a shared_ptr object to reset() anyway. It expects a new T* pointer as input, and shared_ptr is not implicitly convertible to T*, AFAIK. Only the constructor, operator=(), and swap() method allow a shared_ptr as input to invoke sharing. The reset() documentation even warns about this:
If the object pointed to by ptr is already owned, the function results in undefined behavior.
So you would have to use operator= or swap() to assign bar to foo so that bar's X object gets shared.
Try something like this:
template <typename T, class Deleter = std::default_delete<T> >
class My_Deleter
{
Deleter *chained_deleter;
My_Deleter(std::shared_ptr<T>& p)
: chained_deleter(std::get_deleter<Deleter>(p));
{
}
void operator()(T *ptr) const
{
//...
if (chained_deleter)
(*chained_deleter)(ptr);
}
};
std::shared_ptr<X> foo;
{
std::shared_ptr<X> bar(new X, Deleter);
foo = std::shared_ptr<X>(bar, My_Deleter<X, decltype<Deleter> >(bar));
}
Not sure if this would really work, though. It depends on if/how shared_ptr preserves the original deleter when sharing the same object pointer with a different deleter. I have a feeling that bar's original Deleter might get lost when it goes out of scope and not be safely chainable.
Since the deleter for shared_memory can only safely be set on allocation of the memory, that can't be done. Different shared_ptrs can't have different deleters. And in response to doing some work before going out of scope (the reason for the other deleter), if you want to do some extra work before deleting it, then you can just call a separate function that will do the work before the end of the scope. Check some condition for what you might want and do that work before leaving.
Related
There is an array holding unique pointers:
std::array<std::unique_ptr<T, deleter<allocator<T>>>> storage;
where
template<typename ALLOC>
class deleter {
void operator()( void ) { ... }
};
does the deletion as required by unique_ptr. Effectively, it calls the destructor and then deallocates the memory occupied. So far so good.
But there is another deleter:
template<typename T>
class empty_deleter {
void operator()( void ) {}
};
which performs no operation at all - no destruction, no deallocation.
The reason why it exists is to have, in theory, the option to store object owned by such unique_ptr<T, empty_deleter<T>> within the storage array...
The question is - how to achieve that? To make the deleters compatible so that I can assign unique_ptr<T, empty_deleter<T>> to an array of unique_ptr<T, deleter<allocator<T>>> pointers...
I know there are converting constructors within the unique_ptr implementation so in theory, the deleter of one type can be assinged to the unique_ptr being declared with another type but there is a constraint these deleters shall be convertible somehow... could you please advice me how to reach that?
Additionally - once I am successfull in assigning the deleter instance of empty_deleter<T> into unique_ptr<T, deleter<allocator<T>>> whatever how, which operator() is going to be called once the deletion is triggered? The one from empty_deleter<T> or from deleter<allocator<T>>?
You can do this by simply releaseing the pointer from one object and passing it to the other. These two types are incompatible by design. You are not supposed to make them compatible, so you have to do this weird thing to make it work.
Yes, you could make this work by making deleter constructible and assignable from an empty_deleter. But that is bad, because it logically makes no sense.
unique_ptr is supposed to own the object; that's what it is for. You shouldn't want to have a non-owning unique_ptr. If someone gets a unique_ptr, that's supposed to mean they own that thing.
Worse, the very idea of taking a non-owning pointer and claiming ownership of it is highly dubious. If a piece of code does not have the right to delete something (which is what the type unique_ptr<T, empty_deleter<T>> is supposed to mean), it also does not have the right to delegate responsibility for deleting it to someone else.
The code you are trying to write is highly unclear on who owns what (or even what it means to "own" an object) and should be rethought.
As #Nicol Bolas points out, "object owned by such unique_ptr<T, empty_deleter<T>>" is nonsensical. I will answer "how to make a smart pointer that sometimes owns and sometimes doesn't own it's pointee".
None of std::unique_ptr<T, empty_deleter<T>>, std::unique_ptr<T, deleter<allocator<T>>>, nor std::unique_ptr<T, deleter<other_allocator<T>>> are assignable to one another.
If you want to mix and match ownerships of your pointers, you will have to type-erase the deleter. The simplest way is to use the existing function-object type-erasure type, std::function.
template <typename T>
class pmr_unique_ptr : public std::unique_ptr<T, std::function<void(T *)>> {
public:
using unique_ptr::unique_ptr;
// have to supply a deleter
pmr_unique_ptr(pointer) = delete;
pmr_unique_ptr() = delete;
pmr_unique_ptr(std::nullptr_t) = delete;
};
This can be constructed from std::unique_ptr<T, D> so long as D is copyable.
Are smart pointers considered as pointers? And thus can they implicitly used as pointers?
Let's say I have the following class:
class MyClass {
//...
std::shared_ptr<AnotherClass> foo() { /*whatever*/ };
void bar(AnotherClass* a) { /*whatever too*/ };
//...
}
Then can I use MyClass the following way?
// m is an instance of MyClass
m.bar(m.foo());
No they can't be used interchangable. You would get a compiler error in your example. But you can always get the raw pointer by shared_ptr::get().
NO! It would be a terrible API. Yes, you could easily implement it within shared_ptr, but just because you could doesn't mean you should.
Why is it such a bad idea? The plain-pointer-based interface of bar doesn't retain an instance of the shared pointer. If bar happens to store the raw pointer somewhere and then exit, there's nothing that guarantees that the pointer it had stored won't become dangling in the future. The only way to guarantee that would be to retain an instance of the shared pointer, not the raw pointer (that's the whole point of shared_ptr!).
It gets worse: the following code is undefined behavior if foo() returns a pointer instance that had only one reference when foo() returned (e.g. if foo is a simple factory of new objects):
AnotherClass *ptr = m.foo().get();
// The shared_ptr instance returned by foo() is destroyed at this point
m.bar(ptr); // undefined behavior: ptr is likely a dangling pointer here
Here are the options; consider those listed earlier first before considering their successors.
If bar(AnotherClass *) is an external API, then you need to wrap it in a safe way, i.e. the code that would have called Original::bar should be calling MyWrapped::bar, and the wrapper should do whatever lifetime management is necessary. Suppose that there is startUsing(AnotherClass *) and finishUsing(AnotherClass *), and the code expects the pointer to remain valid between startUsing and finishUsing. Your wrapper would be:
class WithUsing {
std::unique_ptr<AnotherClass> owner; /* or shared_ptr if the ownership is shared */
std::shared_ptr<User> user;
public:
WithUsing(std::unique_ptr<AnotherClass> owner, std::Shared_ptr<User> user) :
owner(std::move(owner)), user(std::move(user)) {
user.startUsing(owner.get());
}
void bar() const {
user.bar(owner.get());
}
~WithUsing() {
user.finishUsing(owner.get());
}
};
You would then use WithUsing as a handle to the User object, and any uses would be done through that handle, ensuring the existence of the object.
If AnotherClass is copyable and is very cheap to copy (e.g. it consists of a pointer or two), then pass it by value:
void bar(AnotherClass)
If the implementation of bar doesn't need to change the value, it can be defined to take a const-value (the declaration can be without the const as it doesn't matter there):
void bar(const AnotherClass a) { ... }
If bar doesn't store a pointer, then don't pass it a pointer: pass a const reference by default, or a non-const reference if necessary.
void bar(const AnotherClass &a);
void bar_modifies(AnotherClass &a);
If it makes sense to invoke bar with "no object" (a.k.a. "null"), then:
If passing AnotherClass by value is OK, then use std::optional:
void bar(std::optional<AnotherClass> a);
Otherwise, if AnotherClass takes ownership, passing unique_ptr works fine since it can be null.
Otherwise, passing shared_ptr works fine since it can be null.
If foo() creates a new object (vs. returning an object that exists already), it should be returning unique_ptr anyway, not a shared_ptr. Factory functions should be returning unique pointers: that's idiomatic C++. Doing otherwise is confusing, since returning a shared_ptr is meant to express existing shared ownership.
std::unique_ptr<AnotherClass> foo();
If bar should take ownership of the value, then it should be accepting a unique pointer - that's the idiom for "I'm taking over managing the lifetime of that object":
void bar(std::unique_ptr<const AnotherClass> a);
void bar_modifies(std::unique_ptr<AnotherClass> a);
If bar should retain shared ownership, then it should be taking shared_ptr, and you will be immediately converting the unique_ptr returned from foo() to a shared one:
struct MyClass {
std::unique_ptr<AnotherClass> foo();
void bar(std::shared_ptr<const AnotherClass> a);
void bar_modifies(std::shared_ptr<AnotherClass> a);
};
void test() {
MyClass m;
std::shared_ptr<AnotherClass> p{foo()};
m.bar(p);
}
shared_ptr(const Type) and shared_ptr(Type) will share the ownership,
they provide a constant view and a modifiable view of the object, respectively. shared_ptr<Foo> is also convertible to shared_ptr<const Foo> (but not the other way round, you'd use const_pointer_cast for that (with caution). You should always default to accessing objects as constants, and only working with non-constant types when there's an explicit need for it.
If a method doesn't modify something, make it self-document that fact by having it accept a reference/pointer to const something instead.
Smart pointers are used to make sure that an object is deleted if it is no longer used (referenced).
Smart pointer are there to manage lifetime of the pointer they own/share.
You can think of a wrapper that has a pointer inside. So the answer is no. However you can access to the pointer they own via get() method.
Please note that it is not so difficult to make dangling pointers if you use get method, so if you use it be extra cautious.
From http://en.cppreference.com/w/cpp/memory/unique_ptr:
If T is derived class (sic) of some base B, then std::unique_ptr<T> is
implicitly convertible to std::unique_ptr<B>. The default deleter of
the resulting std::unique_ptr<B> will use operator delete for B,
leading to undefined behavior unless the destructor of B is virtual.
Note that std::shared_ptr behaves differently: std::shared_ptr<B> will
use the operator delete for the type T and the owned object will be
deleted correctly even if the destructor of B is not virtual.
What is the rationale for the difference in behavior upon destruction that is described above? My initial guess would be performance?
Also interesting to know is how an std::shared_ptr<B> is able to call the destructor of a type T in case the destructor on B is non-virtual and can not be called as far as I can see from the context of std::shared_ptr<B>?
std::shared_ptr<X> already has a bunch of overhead over a raw B*.
A shared_ptr<X> basically maintains 4 things. It maintains a pointer-to-B, it maintains two reference counts (a "hard" reference count, and a "soft" one for weak_ptr), and it maintains a cleanup function.
The cleanup function is why shared_ptr<X> behaves differently. When you create a shared_ptr<X>, a function that calls that particular type's destructor is created and stored in the cleanup function managed by the shared_ptr<X>.
When you change types managed (B* becomes C*), the cleanup function remains unchanged.
Because shared_ptr<X> needs to manage the reference counts, the extra overhead of that cleanup function storage is marginal.
For a unique_ptr<B>, the class is almost as cheap as a raw B*. It maintains zero state other than its B*, and its behavior (at destruction) boils down to if (b) delete b;. (Yes, that if (b) is redundant, but an optimizer can figure that out).
In order to support cast-to-base and delete-as-derived, extra state would have to be stored that remembers the unique_ptr is really to a derived class. This could be in the form of a stored pointer-to-deleter, like a shared_ptr.
That would, however, double the size of a unique_ptr<B>, or require it to store data on the heap somewhere.
It was decided that unique_ptr<B> should be zero-overhead, and as such it doesn't support cast-to-base while still calling base's destructor.
Now, you can probably teach unique_ptr<B> to do this by simply adding a deleter type and storing a destruction function that knows the type of thing it is destroying. The above has been talking about the default deleter of unique_ptr, which is stateless and trivial.
struct deleter {
void* state;
void(*f)(void*);
void operator()(void*)const{if (f) f(state);}
deleter(deleter const&)=default;
deleter(deleter&&o):deleter(o) { o.state = nullptr; o.f=nullptr; }
deleter()=delete;
template<class T>
deleter(T*t):
state(t),
f([](void*p){delete static_cast<T*>(p);})
{}
};
template<class T>
using smart_unique_ptr = std::unique_ptr<T, deleter>;
template<class T, class...Args>
smart_unique_ptr<T> make_smart_unique( Args&&... args ) {
T* t = new T(std::forward<Args>(args)...);
return { t, t };
}
live example, where I generate a unique-ptr to derived, store it in a unique-ptr to base, and then reset base. The derived pointer is deleted.
( A simple void(*)(void*) deleter might run into problems whereby the passed in void* would differ in value between the base and derived cases. )
Note that changing the pointer stored in such a unique_ptr without changing the deleter will result in ill advised behavior.
I have read through many questions on SO on custom deleter for shared_ptr and unique_ptr, and the difference between the two. But, I still haven't found any clear answer to this question:
How can one best go about creating a type that acts as a shared_ptr with a custom deleter, similar to how unique_ptr has the deleter as part of the type definition?
For unique_ptr usage, I use a deleter class, that handles deletion of individual types (limiting it to just two types, for brevity):
struct SDL_Deleter {
void operator()( SDL_Surface* ptr ) { if (ptr) SDL_FreeSurface( ptr );}
void operator()( SDL_RWops* ptr ) { if (ptr) SDL_RWclose( ptr );}
};
using SurfacePtr = std::unique_ptr<SDL_Surface, SDL_Deleter>;
using RWopsPtr = std::unique_ptr<SDL_RWops, SDL_Deleter>;
Which can be used with something like
SurfacePtr surface(IMG_Load("image.png"));
And will call SDL_FreeSurface upon destruction.
This is all fine and well. However, how does one go about achieving the same for shared_ptr? Its type is defined as
template< class T > class shared_ptr;
and the way to provide a custom deleter is through the constructor. It doesn't feel right that the user of the shared_ptr wrapper needs to know which pointer type is wrapped, and how that pointer is supposed to be deleted. What would be the best way to achieve the same kind of usage as with the unique_ptr example of above.
In other words, that I could end up with:
SurfaceShPtr surface(IMG_Load("image.png"));
Instead of of something like
SurfaceShPtr surface(IMG_Load("image.png"),
[=](SDL_Surface* ptr){SDL_FreeSurface(ptr);});
Or, just slightly better
SurfaceShPtr surface(IMG_Load("image.png"),
SDL_Deleter());
Is there a way to do this, without having to create a RAII wrapper class (instead of a typedef), adding even more overhead?
If the answer is "this isn't possible". Why not?
The other answer provided here was that something close to what I asked could be done through function returns of unique_ptr with custom deleter, which can be implicitly converted to a shared_ptr.
The answer given was that a deleter defined as a type trait was not possible for std::shared_ptr. The answer suggested as an alternative, to use a function which returns a unique_ptr, implicitly converted to a shared_ptr.
Since this isn't part of the type, it is possible to make a simple mistake, leading to memory leaks. Which is what I wanted to avoid.
For example:
// Correct usage:
shared_ptr<SDL_Surface> s(createSurface(IMG_Load("image.png")));
// Memory Leak:
shared_ptr<SDL_Surface> s(IMG_Load("image.png"));
The concept I want to express is having the deleter as part of the type (which unique_ptr allows), but with the functionality of a shared_ptr. My suggested solution is deriving from shared_ptr, and providing the deleter type as a template argument. This takes up no additional memory, and works in the same way as for unique_ptr.
template<class T, class D = std::default_delete<T>>
struct shared_ptr_with_deleter : public std::shared_ptr<T>
{
explicit shared_ptr_with_deleter(T* t = nullptr)
: std::shared_ptr<T>(t, D()) {}
// Reset function, as it also needs to properly set the deleter.
void reset(T* t = nullptr) { std::shared_ptr<T>::reset(t, D()); }
};
Together with a deleter class (Thanks Jonathan Wakely. Way cleaner than my macro (now removed)):
struct SDL_Deleter
{
void operator()(SDL_Surface* p) const { if (p) SDL_FreeSurface(p); }
void operator()(SDL_RWops* p) const { if (p) SDL_RWclose(p); }
};
using SurfacePtr = std::unique_ptr<SDL_Surface, SDL_Deleter>;
using SurfaceShPtr = shared_ptr_with_deleter<SDL_Surface, SDL_Deleter>;
using RWopsPtr = std::unique_ptr<SDL_RWops, SDL_Deleter>;
using RWopsShPtr = shared_ptr_with_deleter<SDL_RWops, SDL_Deleter>;
Instances with SurfaceShPtr members are type guaranteed to clean up properly, the same as for SurfacePtr, which is what I wanted.
// Correct Usage (much harder to use incorrectly now):
SurfaceShPtr s(IMG_Load("image.png"));
// Still correct usage
s.reset(IMG_Load("other.png"));
I'll leave this up for a while, for comments, etc, without accepting the answer. Maybe there are even more dangerous caveats I've missed (having a non-virtual destructor not being one, as the parent shared_ptr is given charge of the deletion).
A typedef is a static, compile-time feature.
A deleter passed to a shared_ptr is a dynamic, run-time property. The deleter is "type-erased" and is not part of the shared_ptr interface.
Therefore you can't declare a typedef to represent an alternative deleter, you just pass one to the constructor.
What would be the best way to achieve the same kind of usage as with the unique_ptr example of above.
You could use functions to create the resources and return them in a shared_ptr
shared_ptr<SDL_Surface> create_sdl_surface(const char* s)
{
return shared_ptr<SDL_Surface>(IMG_load(s), SDL_FreeSurface);
}
But I would have those functions return a unique_ptr instead, which can be converted to shared_ptr, as below.
I would get rid of the macro and do something like this:
// type with overloaded functions for freeing each resource type
struct SDL_deleter
{
void operator()(SDL_Surface* p) const { if (p) SDL_FreeSurface(p); }
void operator()(SDL_RWops* p) const { if (p) SDL_RWclose(p); }
// etc.
};
// a unique_ptr using SDL_deleter:
template<typename P>
using SDL_Ptr = std::unique_ptr<P, SDL_deleter>;
// typedefs for the common ptr types:
using SurfacePtr = SDL_ptr<SDL_Surface>;
using RWopsPtr = SDL_ptr<SDL_RWops>;
// etc.
To answer the shared_ptr part of your question, define functions that create resources and return them in a SDL_ptr:
SurfacePtr createSurface(const char* s) { return SurfacePtr(IMG_load(s)); }
RWopsPtr createRWops([...]) { return RWopsPtr([...]); }
// etc.
Then you can easily create a shared_ptr from the result of those functions:
shared_ptr<SDL_Surface> s = createSurface("image.png");
The shared_ptr automatically acquires the right deleter from the unique_ptr.
In the recent overload journal under the topic Enforcing the rule of zero, the authors describe how we can avoid writing the Rule of five operators as the reasons for writing them are:
Resource management
Polymorphic deletion
And both these can be taken care of by using smart pointers.
Here I am specifically interested in the second part.
Consider the following code snippet:
class Base
{
public:
virtual void Fun() = 0;
};
class Derived : public Base
{
public:
~Derived()
{
cout << "Derived::~Derived\n";
}
void Fun()
{
cout << "Derived::Fun\n";
}
};
int main()
{
shared_ptr<Base> pB = make_shared<Derived>();
pB->Fun();
}
In this case, as the authors of the article explain, we get polymorphic deletion by using a shared pointer, and this does work.
But if I replace the shared_ptr with a unique_ptr, I am no longer able to observe the polymorphic deletion.
Now my question is, why are these two behaviors different? Why does shared_ptr take care of polymorphic deletion while unique_ptr doesn't?
You have your answer here: https://stackoverflow.com/a/22861890/2007142
Quote:
Once the last referring shared_ptr goes out of scope or is reset, ~Derived() will be called and the memory released. Therefore, you don't need to make ~Base() virtual. unique_ptr<Base> and make_unique<Derived> do not provide this feature, because they don't provide the mechanics of shared_ptr with respect to the deleter, because unique pointer is much simpler and aims for the lowest overhead and thus is not storing the extra function pointer needed for the deleter.
It'll work if you use the C++14 make_unique or write your own one like in Yakk's answer. Basically the difference between the shared pointer behavior is that you got:
template<
class T,
class Deleter = std::default_delete<T>
> class unique_ptr;
for unique_pointer and as you can see, the deleter belongs to the type. If you declare a unique_pointer<Base> it'll always use std::default_delete<Base> as default. But make_unique will take care of using the correct deleter for your class.
When using shared_ptr you got:
template< class Y, class Deleter >
shared_ptr( Y* ptr, Deleter d );
and other overloads as constructor. As you can see the default deleter for unique_ptr depends on the template parameter when declaring the type (unless you use make_unique) whilst for shared_ptr the deleter depends on the type passed to the constructor.
You can see a version that allows polymorphic delete without virtual destructor here (this version should also work in VS2012). Note that it is quite a bit hacked together and I'm currently not sure what the behavior of unique_ptr and make_shared in C++14 will be like, but I hope they'll make this easier. Maybe I'll look into the papers for the C++14 additions and see if something changed if I got the time later.
template<typename T>
using smart_unique_ptr=std::unique_ptr<T,void(*)(void*)>;
template<class T, class...Args> smart_unique_ptr<T> make_smart_unique(Args&&...args) {
return {new T(std::forward<Args>(args)...), [](void*t){delete (T*)t;}};
}
The problem is that the default deleter for unique_ptr calls delete on the stored pointer. The above stores a deleter that knows the type at construction, so when copied to base class unique_ptr will still delete as the child.
This adds modest overhead, as we have to dereference a pointer. In addition it denormalizes the type, as default constructed smart_unique_ptrs are now illegal. You can fix this with some extra work (replace a raw function pointer with a semi smart functor that at least does not crash: the function pointer, however, should be asserted to exist if the unique is non-empty when the deleter is invoked).