I am making an class which is managed by a unique_ptr, but for various reasons I need to give implementations access to a raw pointer to the object. However I want to ensure that users don't inadvertently delete the underlying object. I have come up with the following example code:
(It is part of a tree structure, and I need to be able to look at members of tree nodes without actually detaching them. shared_ptr seems like overkill in this situation.)
#include <memory>
using namespace std;
class unOnly
{
~unOnly() {}
public:
unOnly() {}
friend class default_delete<unOnly>;
};
int main()
{
unique_ptr<unOnly> ptr(new unOnly());
}
This compiles for me in gcc 4.4.5. However, can I be sure that in all implementations default_delete is what actually deletes the object, as opposed to some private implementation class? Should I write my own deleter to be sure?
Why not
class unOnly
{
unOnly() {}
~unOnly() {}
struct deleter { void operator()(unOnly* x) { delete x; }};
public:
typedef std::unique_ptr<unOnly, deleter> handle;
static handle create() { return handle(new unOnly); }
};
auto x = unOnly::create();
? Or even
class unOnly
{
~unOnly() {}
struct deleter { void operator()(unOnly* x) { delete x; }};
public:
unOnly() {}
typedef std::unique_ptr<unOnly, deleter> handle;
};
unOnly::handle x(new unOnly);
(I prefer the former, but the latter is perhaps more in the spirit of what you're asking for)
The point of unique_ptr (besides having an object which owns it's pointer) is that you can pass it a custom deleter, so it makes sense just to write one instead of doing something else, which seems unnecessarily complex.
Quoting from the standard:
20.7.1 Class template unique_ptr
6. [...]
template<class T, class D = default_delete<T>> class unique_ptr;
20.7.1.1.1
1 The class template default_delete serves as the default deleter (destruction policy) for the class template unique_ptr.
So, it seems implementations of unique_ptr are required to use default_delete as the default deleter.
EDIT:
But this doesn't mean your approach is foolproof, see #RMartinhoFernandes' comment below.
Related
I was reading the Smart Pointer Programming Techniques provided in the boost documentation.
In the Section "using abstract classes for implementation hiding", they provide a nice idiom to fully hide an implementation behind pure virtual interface. For example:
// Foo.hpp
#include <memory>
class Foo {
public:
virtual void Execute() const = 0;
protected:
~Foo() = default;
};
std::shared_ptr<const Foo> MakeFoo();
and
// Foo.cpp
#include "Foo.hpp"
#include <iostream>
class FooImp final
: public Foo {
public:
FooImp() = default;
FooImp(const FooImp&) = delete;
FooImp& operator=(const FooImp&) = delete;
void Execute() const override {
std::cout << "Foo::Execute()" << std::endl;
}
};
std::shared_ptr<const Foo> MakeFoo() {
return std::make_shared<const FooImp>();
}
Regarding the protected, non-virtual destructor in class Foo, the document states:
Note the protected and nonvirtual destructor in the example above. The
client code cannot, and does not need to, delete a pointer to X; the
shared_ptr<X> instance returned from createX will correctly call
~X_impl.
which I believe I understand.
Now, it seems to me that this nice idiom could be used to produce a singleton-like entity if a factory function returned std::unique_ptr<Foo>; the user would be forced to move the pointer, with a compile-time guarantee that no copies exist.
But, alas, I cannot make the code work unless I change ~Foo() = default from protected to public, and I do not understand why.
In other words, this does not work:
std::unique_ptr<const Foo> MakeUniqueFoo() {
return std::make_unique<const FooImp>();
}
My questions:
Could you explain me why do I need to make public ~Foo() = default?
Would it be dangerous to just remove protected?
Is the singleton-like idea even worth it?
The issue has to do with how deleters work in smart pointers.
In shared_ptr, the deleter is dynamic. When you have std::make_shared<const FooImp>();, the deleter in that object will call ~FooImpl() directly:
The object is destroyed using delete-expression or a custom deleter that is supplied to shared_ptr during construction.
That deleter will be copied onto the shared_ptr<const Foo> when it's created.
In unique_ptr, the deleter is part of the type. It's:
template<
class T,
class Deleter = std::default_delete<T>
> class unique_ptr;
So when you have unique_ptr<const Foo>, that will call ~Foo() directly - which is impossible since ~Foo() is protected. That's why when you make Foo() public, it works. Works, as in, compiles. You would have to make it virtual too - otherwise you'd have undefined behavior by only destructing the Foo part of FooImpl.
It's not dangerous. Unless you forget to make the destructor virtual, which, it bears repeating, will cause undefined behavior.
This isn't really singleton-like. As to whether or not it's worth it? Primarily opinion based.
Every shared_ptr stores 4 things: a pointer, a strong reference count, a weak reference count, and a deleter.
The deleter gets the type from which the shared_ptr was constructed from, and deletes that type, not the exposed type. If you cast it to a base shared_ptr, the derived deleter is still stored.
unique_ptr does not, by default, store such a stateful deleter.
The design reason behind this is that a shared_ptr is already managing extra resources: adding that deleter is cheap, given that you are already managing the reference counts.
For unique_ptr, without a stateful deleter, the overhead for it is basically identical to a raw pointer. Adding a stateful deleter by default would make unique_ptrs significantly more expensive.
While they are both smart pointers, unique_ptr is really minimal, while shared_ptr is far more complex.
You can get around this by appending a stateful deleter to unique_ptr.
struct stateful_delete {
void const* ptr = nullptr;
void(*f)(void const*) = nullptr;
template<class T>
stateful_delete(T const* t):
ptr(t),
f([](void const* ptr){
delete static_cast<T const*>(ptr);
})
{}
template<class T>
void operator()(T*)const{
if (f) f(ptr);
}
};
template<class T>
using unique_ptr_2 = std::unique_ptr<T, stateful_delete>;
template<class T>
unique_ptr_2<T> unique_wrap_2(T* t) {
return {t, t};
}
template<class T, class...Args>
unique_ptr_2<T> make_unique_2(Args&&...args) {
return unique_wrap( new T(std::forward<Args>(args)...) );
}
such unique_ptr_2 are 3 times as large as a unique_ptr. They do not do an extra allocation (unlike shared_ptr). And they will work with your non-virtual protected ~Foo with a public ~FooImpl.
You could reduce the size of unique_ptr_2 down to 2 pointers if we use the make_shared technique of doing a unified allocation, and store the equivalent of the ptr and f on the heap. I'm uncertain if that complexity is worth the savings.
Based on Barry's answer an alternative to making it public is defining your own deleter which has access to your class' ~Foo() method.
Example (tried with VS2013):
template <typename T>
class deleter
{
public:
void operator()(T* a)
{
// Explicitly call the destructor on a.
a->~A();
}
};
class A {
friend class deleter<A>; // Grant access to the deleter.
protected:
~A() {
// Destructor.
}
};
std::unique_ptr<A, deleter<A>> MakeA()
{
return std::unique_ptr<A, deleter<A>>(new A());
}
I am writing a class that uses two objects created using a C interface. The objects look like:
typedef struct... foo_t;
foo_t* create_foo(int, double, whatever );
void delete_foo(foo_t* );
(similarly for bar_t). Because C++11, I want to wrap these in a smart pointer so I don't have to write any of the special methods. The class will have unique ownership of the two objects, so unique_ptr logically make sense... but I would still have to write a constructor:
template <typename T>
using unique_ptr_deleter = std::unique_ptr<T, void(*)(T*)>;
struct MyClass {
unique_ptr_deleter<foo_t> foo_;
unique_ptr_deleter<bar_t> bar_;
MyClass()
: foo_{nullptr, delete_foo}
, bar_{nullptr, delete_bar}
{ }
~MyClass() = default;
void create(int x, double y, whatever z) {
foo_.reset(create_foo(x, y, z));
bar_.reset(create_bar(x, y, z));
};
On the flip side, with shared_ptr, I wouldn't have to write a constructor, or use a type alias, since I could just pass in delete_foo into reset() - although that would make my MyClass copyable and I don't want that.
What is the correct way to write MyClass using unique_ptr semantics and still adhere to Rule of Zero?
Your class doesn't need to declare a destructor (it will get the correct default implementation whether or not you declare it defaulted), so still obeys the "Rule of Zero".
However, you might improve this by making the deleters function objects, rather than pointers:
template <typename T> struct deleter;
template <> struct deleter<foo_t> {
void operator()(foo_t * foo){delete_foo(foo);}
};
template <> struct deleter<bar_t> {
void operator()(bar_t * bar){delete_bar(bar);}
};
template <typename T>
using unique_ptr_deleter = std::unique_ptr<T, deleter<T>>;
This has a few benefits:
the unique_ptr doesn't need to store an extra pointer
the delete function can be called directly, rather than via a pointer
you don't need to write a constructor; the default constructor will do the right thing.
I've read an "Item" about shared_ptr in Scott Meyers' book "Effective Modern C++" where he says the following:
The usual control block implementation is more
sophisticated than you might expect. It makes use of inheritance, and there’s even a virtual function. (It’s used to ensure that the pointed-to object is properly destroyed.)
That means that using std::shared_ptrs also incurs the cost of the machinery for the virtual function used by the control block.
Then he doesn't explain what a virtual function does exactly. As far as I know, the proper way of deleting a pointed object is using deleters or type erasure. So, please explain what this is about.
The virtual function is required to ensure the object being shared is appropriately deleted. Unlike a unique_ptr, shared_ptr does not require full knowledge of the type when its template is instantiated. E.g. you can do this:
class Foo;
std::shared_ptr<Foo> foo = make_foo();
Note that in the code above we don't have the complete Foo type, just the forward declaration. If we let foo go out of scope, the object it is pointing to will be correctly deleted because when Foo was created in make_foo, a deleter was also created which knows the complete type of Foo, and hence can call the appropriate destructors. (E.g. Perhaps make_foo creates a Bar that inherits from Foo and returns that. shared_ptr's will handle this fine.)
The function on the deleter object that shared_ptr creates to manage the deletion of Foo will be virtual, allowing shared_ptr to call the correct destructor.
Roughly this could be something like this:
struct deleter_interface {
virtual void ~deleter_interface = default;
virtual void dispose() = 0;
};
template <typename T>
struct deleter : deleter_interface {
T* ptr_;
deleter(T* ptr) : ptr_(ptr) {}
virtual void dispose() { delete ptr_; }
};
template <typename T>
shared_ptr {
T* ptr_;
deleter_interface* deleter_;
...
};
template <typename T>
shared_ptr<T>::shared_ptr<T>(T* ptr)
: ptr_(ptr), deleter_(new deleter<T>(ptr)) {}
template <typename T>
shared_ptr<T>::~shared_ptr<T>() { deleter_->dispose(); delete deleter_; }
While this seems more complicated that is strictly necessary, it is to allow shared_ptr to be used without the complete type. For example what if you wanted to do this:
In a.h:
struct Foo;
std::shared_ptr<Foo> a = make_object();
// ... let a go out of scope
And in a.cc:
struct Foo { ... };
struct Bar : Foo { ... };
std::shared_ptr<Foo> make_object() { return std::shared_ptr<Foo>(new Bar); }
If we didn't have the virtual function used in the deleter code, then Bar would not be destructed correctly. With the virtual function it doesn't matter that the header (a.h) never sees the definition of Foo or Bar.
I was reading the latest Overload (link) and decided to test out the statement at page 8:
shared_ptr will properly invoke B’s destructor on scope exit, even
though the destructor of A is not virtual.
I am using Visual Studio 2013, compiler v120:
#include <memory>
#include <iostream>
struct A {
~A() { std::cout << "Deleting A"; }
};
struct B : public A
{
~B() { std::cout << "Deleting B"; }
};
int main()
{
std::shared_ptr<A> ptr = std::make_shared<B>();
ptr.reset();
return 0;
}
This works as expected and prints out "Deleting BDeleting A"
The article seems to imply that this should also work with std::unique_ptr:
There is almost no need to manage your own resources so resist the
temptation to implement your own copy/assign/move construct/move
assign/destructor functions.
Managed resources can be resources inside
your class definition or instances of your classes themselves.
Refactoring the code around standard containers and class templates
like unique_ptr or shared_ptr will make your code more readable and
maintainable.
However, when changing
std::shared_ptr<A> ptr = std::make_shared<B>();
to
std::unique_ptr<A> ptr = std::make_unique<B>();
the program will only output "Deleting A"
Did I misunderstand the article and the behavior is intended by the standard?
Is it a bug with the MSVC compiler?
shared_ptr and unique_ptr are different. make_shared will create a type erased deleter object on invocation while with unique_ptr the deleter is part of the type. Therefore the shared_ptr knows the real type when it invokes the deleter but the unique_ptr doesn't. This makes unique_ptr's much more efficient which is why it was implemented this way.
I find the article a bit misleading actually. I do not consider it to be good advice to expose copy constructors of a base class with virtual functions, sounds like a lot of slicing problems to me.
Consider the following situation:
struct A{
virtual void foo(){ std::cout << "base"; };
};
struct B : A{
virtual void foo(){ std::cout << "derived"; };
};
void bar(A& a){
a.foo(); //derived
auto localA = a; //poor matanance porgrammer didn't notice that a is polymorphic
localA.foo(); //base
}
I would personally advocate non-intrusive polymorphism http://isocpp.org/blog/2012/12/value-semantics-and-concepts-based-polymorphism-sean-parent for any new higherarchies, it sidesteps the problem entirely.
This behaviour is according to the standard.
The unique_ptr is designed to have no performance overhead compared to the ordinary new/delete.
The shared_ptr has the allowance to have the overhead, so it can be smarter.
According to the standard [20.7.1.2.2, 20.7.2.2.2], the unique_ptr calls delete on the pointer returned by get(), while the shared_ptr deletes the real object it holds - it remembers the proper type to delete (if properly initialized), even without a virtual destructor.
Obviously, the shared_ptr is not all-knowing, you can trick it to behave badly by passing a pointer to the base object like this:
std::shared_ptr<Base> c = std::shared_ptr<Base> { (Base*) new Child() };
But, that would be a silly thing to do anyhow.
You could do something similar with unique_ptr, but since its deleter type is determined statically, you need to statically maintain the proper deleter type. i.e. (Live demo at Coliru):
// Convert given pointer type to T and delete.
template <typename T>
struct deleter {
template <typename U>
void operator () (U* ptr) const {
if (ptr) {
delete static_cast<T*>(ptr);
}
}
};
// Create a unique_ptr with statically encoded deleter type.
template <typename T, typename...Args>
inline std::unique_ptr<T, deleter<T>>
make_unique_with_deleter(Args&&...args) {
return std::unique_ptr<T, deleter<T>>{
new T(std::forward<Args>(args)...)
};
}
// Convert a unique_ptr with statically encoded deleter to
// a pointer to different type while maintaining the
// statically encoded deleter.
template <typename T, typename U, typename W>
inline std::unique_ptr<T, deleter<W>>
unique_with_deleter_cast(std::unique_ptr<U, deleter<W>> ptr) {
T* t_ptr{ptr.release()};
return std::unique_ptr<T, deleter<W>>{t_ptr};
}
// Create a unique_ptr to T with statically encoded
// deleter for type U.
template <typename T, typename U, typename...Args>
inline std::unique_ptr<T, deleter<U>>
make_unique_with_deleter(Args&&...args) {
return unique_with_deleter_cast<T>(
make_unique_with_deleter<U>(std::forward<Args>(args)...)
);
}
It's a bit awkward to use:
std::unique_ptr<A, deleter<B>> foo = make_unique_with_deleter<A, B>();
auto improves the situation:
auto bar = make_unique_with_deleter<A, B>();
It doesn't really buy you much, since the dynamic type is encoded right there in the static type of the unique_ptr. If you're going to carry the dynamic type around, why not simply use unique_ptr<dynamic_type>? I conjecture such a thing may have some use in generic code, but finding an example of such is left as an exercise to the reader.
The technique std::shared_ptr employs to do the magic is called type erasure. If you are using gcc, try to find the file bits/shared_ptr_base.h and check the implementation. I'm using gcc 4.7.2.
unique_ptr is designed for minimum overhead and does not use type erasure to remember the actual type of the pointer it holds.
Here is a great discussion on this topic: link
EDIT: a simple implementation of shared_ptr to showcase how type erasure is achieved.
#include <cstddef>
// A base class which does not know the type of the pointer tracking
class ref_counter_base
{
public:
ref_counter_base() : counter_(1) {}
virtual ~ref_counter_base() {}
void increase()
{
++counter_;
}
void release()
{
if (--counter_ == 0) {
destroy();
delete this;
}
}
virtual void destroy() = 0;
private:
std::size_t counter_;
};
// The derived class that actually remembers the type of
// the pointer and deletes the pointer on destroy.
template <typename T>
class ref_counter : public ref_counter_base
{
public:
ref_counter(T *p) : p_(p) {}
virtual void destroy()
{
delete p_;
}
private:
T *p_;
};
template <typename T>
class shared_ptr
{
public:
shared_ptr(T *p)
: p_(p)
, counter_(new ref_counter<T>(p))
{
}
// Y* should be implicitely convertable to T*,
// i.e. Y is derived from T
template <typename Y>
shared_ptr(Y &other)
: p_(other.get())
, counter_(other.counter())
{
counter_->increase();
}
~shared_ptr()
{
counter_->release();
}
T* get() { return p_; }
ref_counter_base* counter() { return counter_; }
private:
T *p_;
ref_counter_base *counter_;
};
I've been working on a way to prevent user of using a class without smart pointers. Thus, forcing them to have the object being heap allocated and managed by smart pointers.
In order to get such a result, I've tried the following :
#include <memory>
class A
{
private :
~A {}
// To force use of A only with std::unique_ptr
friend std::default_delete<A>;
};
This work pretty well if you only want your class users being capable of manipulating instance of your class through std::unique_ptr. But it doesn't work for std::shared_ptr. So I'd like to know if you had any ideas to get such a behavior. The only solution that I've found is doing the following (using friend std::allocator_traits<A>; was unsufficient) :
#include <memory>
class A
{
private :
~A {}
// For std::shared_ptr use with g++
friend __gnu_cxx::new_allocator<A>;
};
But this solution is not portable. Maybe I'm doing it the wrong way.
Create a friend'd factory function that returns a std::unique_ptr<A>, and make your class have no accessible constructors. But make the destructor available:
#include <memory>
class A;
template <class ...Args>
std::unique_ptr<A> make_A(Args&& ...args);
class A
{
public:
~A() = default;
private :
A() = default;
A(const A&) = delete;
A& operator=(const A&) = delete;
template <class ...Args>
friend std::unique_ptr<A> make_A(Args&& ...args)
{
return std::unique_ptr<A>(new A(std::forward<Args>(args)...));
}
};
Now your clients can obviously get a unique_ptr<A>:
std::unique_ptr<A> p1 = make_A();
But your clients can just as easily get a shared_ptr<A>:
std::shared_ptr<A> p2 = make_A();
Because std::shared_ptr can be constructed from a std::unique_ptr. And if you have any user-written smart pointers, all they have to do to be interoperable with your system is create a constructor that takes a std::unique_ptr, just like std::shared_ptr has, and this is very easy to do:
template <class T>
class my_smart_ptr
{
T* ptr_;
public:
my_smart_ptr(std::unique_ptr<T> p)
: ptr_(p.release())
{
}
// ...
};
As there is no general term "smart pointer" what you want is not possible.
What you can do, is supporting some known set of smart pointers. The usual solution starts as yours, making ctor or dtor private, and adds factory functions. That can return the instance packed with your desired smart pointers. If you just want to support unique_ptr and shared_ptr, that meaans two factory functions, hardly too much. (note that those pointers allow smuggling out the raw pointer through simple interface so the control is not full.)