Can I have non owning shared pointers? - c++

Intro
The question arose from the need for a conditional interface. It may be so that I fell into an XY problem, but (bottom line) I ended up needing a shared pointer that would ( based on a runtime choice ) either manage or not (own or not) a resource.
Work so far
The following are some thoughts on a non owning shared pointer
Using placement new, eg:
struct MyStruct {};
MyStruct ms1;
std::shared_ptr<MyStruct> sp(new(&ms1) MyStruct);
Using a dummy deleter
std::shared_ptr<MyStruct> spn(new MyStruct, [](MyStruct*){});
Question
Is there a standard proposed way?
Is there a "don't do it" rule?
Is there at least a better way?
Notes
My class layout (where the non owning shared pointer will be used) looks like this:
template<typename T>
struct blah
{
shared_ptr<T> _m;
};
Now, the _m member may or may not own a resource based on a runtime choice. The reason I'm not using weak_ptr is because _m may actually be an owning pointer.

The placement new is obviously UB, since it will in your snippet attempt to delete something on the stack. The empty deleter version will work but will allocate a reference counting block.
The trick is to use the crazy (ok, aliasing) constructor of shared_ptr:
template< class Y >
shared_ptr( const shared_ptr<Y>& r, T *ptr );
that constructs a shared_ptr owning what r owns, but pointing to what ptr points to, i.e.:
std::shared_ptr<MyStruct> sp(std::shared_ptr<MyStruct>(), p);
This is guaranteed noexcept by the standard, and will not allocate anything. The standard even has a note that says
[ Note: This constructor allows creation of an empty shared_ptr
instance with a non-null stored pointer. —end note ]

The placement new will give you undefined behaviour (and likely a crash) on destruction - it will promptly call delete on something which was not created with new.
I'd go with the no-op deleter. The design may seem odd, but if you need such behaviour (and document it enough), it will work. I used something like this in one of my projects for a while, but then I got rid of the need for it.

Related

How to use a shared_ptr with a pointer to struct that should not be freed

Currently I'm using some functions from the glib library. With glib also comes the gio. glib is a C library and therefore I need to delete some structures that I create.
for many of the objects I create a smartpointer eg:
std::shared_ptr<GAsyncQueue> my_queue = std::shared_ptr<GAsyncQueue>(g_async_queue_create(), g_async_queue_unref);
For this creates a shared pointer to an GAsyncQueue and this is safely destroys the queue on its end of its life.
However, I encounter a problem when I obtain a pointer from the gio library that I should not free. In the following code my_connection is a GSocketClient which implements (in glib speak) GIOStream.
std::shared_ptr<GInputStream> my_input_stream =
std::shared_ptr<GInputStream> (
g_io_stream_get_input_stream(G_IO_STREAM(my_connection.get()))
);
Because the documentation on GIOStream mentions, that the pointer obtained with g_io_stream_get_input_stream() should not be freed. That is because it is owned by the my_connection instance.
I thought about creating a lamda for the destroy object, the second parameter of a shared pointer object. eg auto deleter = [](GInputStream* ptr) {}; and then give that lambda as destroy function to the shared pointer, but that feels a kind of stupid.
Well, alternative to no-op deleter might be using aliasing shared pointer
template <class U> shared_ptr (const shared_ptr<U>& x, element_type* p) noexcept;
It shares x, but after get() you'll get back p.
Discussion: What is shared_ptr's aliasing constructor for?
You probably just don't need a std::shared_ptr. And you probably don't even need a pointer.
As I read your question and comments, I don't see any point against
auto& my_input_stream = *( g_io_stream_get_input_stream(G_IO_STREAM(my_connection.get())) )
It is true that pointers allow optional data. However, it's also true that it's mostly used the wrong way. Having
void foo( type* ptr)
{
if (!ptr)
throw exception;
}
often doesn't make sense. If the function has to to work on concrete data, allowing a NULL parameter is only useful if you then worry about providing that data. Otherwise, just require a reference (possibly const) to the object.
Smart pointers are useful; but they're still pointers. Avoiding them altogether, if possible, is even better.
From the comments:
However, a reference must always be initialized
Absolutely. Since C++11 though we've got std::reference_wrapper which can also be reassinged and stored in containers.
You can use a deleter type that does nothing, but it will need to be passed as an argument to the shared_ptr's constructor
struct DoNothing {
template <typename T>
void operator()(T*) const noexcept { }
};
When creating a shared_ptr you will need to create one of these deleters and pass it in the constructor (as you're doing with the lambda). You can make this easier on yourself with an intermediate function
template <typename T>
std::shared_ptr<T> non_deleting_shared_ptr(T* ptr) {
return {ptr, DoNothing};
}
auto my_input_stream =
non_deleting_shared_ptr(
g_io_stream_get_input_stream(G_IO_STREAM(my_connection.get()));
However the bigger question is why you're using smart pointers when you don't want ownership to be a part of it. You'd almost certainly be better off with just a GAsyncQueue*, unless of course you're in a situation where you have a shared_ptr that needs to free sometimes. Like a data member maybe?

C++ Rule of Zero : polymorphic deletion and unique_ptr behavior

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).

Missing shared_ref

While working with std::shared_ptr a lot I kind of miss a shared_ref implementation. That is a specialization of shared_ptr, which guarantees, that it never wraps a nullptr (given right usage, of course).
I kind of wonder why it is not in the C++11 standard. Are there any mayor problems when implementing it? On the top of my head I cannot think of any.
EDIT:
I would expect to have an interface similar to:
template <typename T>
class shared_ref {
public:
shared_ref( T&& ref );
T& get();
T* operator&() const;
template< class Y >
void reset( Y&& obj );
long use_count() const;
bool unique() const;
void swap( shared_ref& r );
};
Are there any mayor problems when implementing it?
Here's one: you can't take ownership of a reference. The whole point of a smart pointer is to claim ownership of the pointer itself. shared_ref can't work because you can't control the lifetime of a reference.
And no, this isn't going to fly either:
shared_ref( T&& ref ) : p(&ref) {}
The user may have given you a stack variable, which now means you have "shared" ownership between this object and a stack variable. And stack variables cannot share ownership with something.
You can only control the lifetime of a pointer. And pointers can be NULL. Therefore, the only thing you can do is a runtime check to see if a pointer is NULL.
The absolute best you can do is an interface equivalent to shared_ptr except that it has no default constructor and throws in the event of being given NULL. Is that really worth creating a whole new pointer type over?
The C++ Core Guidelines support library has the not_null template, which can be applied to most pointer-like types. So you can use not_null<shared_ptr> when you want to verify that a pointer isn't NULL, but only once when it enters use. After the initial creating of the pointer, it doesn't need to check again.
Granted, you can't force other people to use them, but use of the type consistently will resolve the issue.
There are only two ways for a shared_ptr to be null - either it was default constructed, or it was assigned a null value at some point. Since you already agree it doesn't make sense to default construct your hypothetical shared_ref class, that leaves only the second condition.
If you tried to assign a nullptr to your shared_ref object, what would you expect to happen? Should it throw an error? It's trivial to do the same thing with a regular shared_ptr using a simple template function:
template<typename T>
T* notnull(T* ptr)
{
if (ptr == std::nullptr)
throw std::invalid_argument(std::string("nullptr"));
return ptr;
}
std::shared_ptr<int> pint = notnull(GetIntPtr());
Generally things aren't added to the standard unless there's a compelling need with no easy workarounds.

C++ pointer container with reference counting

I need a collection in which i can store heap-allocated objects having virtual functions.
I known about boost::shared_ptr, std::unique_ptr (C++11) and boost::ptr_(vector|list|map), but they doesn't solve duplicate pointer problem.
Just to describe a problem - i have a function which accepts heap-allocated pointer and stores it for future use:
void SomeClass::add(T* ptr)
{
_list.push_back(ptr);
}
But if i call add twice with same parameter ptr - _list will contain two pointers to same object and when _list is destructed multiple deletion of same object will occur.
If _list will count pointer which he stores and uses them at deletion time then this problem will be solved and objects will not be deleted multiple times.
So the question is:
Does somebody knows some library with collections (vector,list,map in essence) of pointer with auto-delete on destruction and support of reference counting?
Or maybe i can solve this problem using some other technique?
Update:
I need support of duplicate pointers. So i can't use std::set.
As Kerrek SB and Grizzly mentioned - it is a bad idea to use raw pointers in general and suggests to use std::make_shared and forget about instantiation via new. But this is responsibility of client-side code - not the class which i designs. Even if i change add signature (and _list container of course) to
void SomeClass::add(std::shared_ptr<T> ptr)
{
_list.push_back(ptr);
}
then somebody (who doesn't know about std::make_shared) still can write this:
SomeClass instance;
T* ptr = new T();
instance.add(ptr);
instance.add(ptr);
So this is not a full solution which i wait, but useful if you write code alone.
Update 2:
As an alternative solution i found a clonning (using generated copy constructor). I mean that i can change my add function like this:
template <typename R>
void SomeClass::add(const R& ref)
{
_list.push_back(new R(ref));
}
this will allow virtual method (R - class which extends some base class (interface)) calls and disallow duplicate pointers. But this solution has an overhead for clone.
Yes: std::list<std::shared_ptr<T>>.
The shared pointer is avaiable from <memory>, or on older platforms from <tr1/memory>, or from Boost's <boost/shared_ptr.hpp>. You won't need to delete anything manually, as the shared pointer takes care of this itself. You will however need to keep all your heap pointers inside a shared pointer right from the start:
std::shared_ptr<T> p(new T); // legacy
auto p = std::make_shared<T>(); // better
If you another shared pointer to the same object, make a copy of the shared pointer (rather than construct a new shared pointer from the underlying raw pointer): auto q = p;
The moral here is: If you're using naked pointers, something is wrong.
Realize that smart pointers are compared by comparing the underlying container. So you can just use a std::set of whatever smartpointer you prefer. Personally I use std::unique_ptr over shared_ptr, whenever I can get away with it, since it makes the ownership much clearer (whoever holds the unique_ptris the owner) and has much lower overhead too. I have found that this is enough for almost all my code. The code would look something like the following:
std::set<std::unique_ptr<T> > _list;
void SomeClass::add(T* ptr)
{
std::unique_ptr<T> p(ptr);
auto iter = _list.find(p);
if(iter == _list.end())
_list.insert(std::move(p));
else
p.release();
}
I'm not sure right now if that is overkill (have to check if insert is guaranteed not to do anything, if the insertion fails), but it should work. Doing this with shared_ptr<T> would look similar, although be a bit more complex, due to the lack of a relase member. In that case I would probably first construct a shared_ptr<T> with a do nothing deleter too pass to the call to find and then another shared_ptr<T> which is actually inserted.
Of course personally I would avoid doing this and always pass around smart pointers when the ownership of a pointer changes hands. Therefore I would rewrite SomeClass::add as void SomeClass::add(std::unique_ptr<T> ptr) or void SomeClass::add(std::shared_ptr<T> ptr) which would pretty much solve the problem of having multiple instances anyways (as long as the pointer is always wrapped).

Question about smart pointers in C++

Is auto_ptr good to work with only for local variables?
If I need to work with classes too, do i need copied pointer ?
The auto_ptr destructor does deallocate the memory, so you are correct. Once you leave the auto_ptr's scope the thing you're pointing to will go away. You might be looking for something like a shared_ptr, which is a reference counted smart pointer. It will be part of the next C++ standard. Boost has an implementation of it, and it's also part of the TR1 library. If your compiler supports std::tr1 entities then you should have the shared_ptr.
Edit
As pointed out in comments, auto_ptr copy semantics cause a transfer of ownership that does not necessarily result in deletion of the object. So, an auto_ptr type variable can be assigned to another, and could be used as a function return value. The key with auto_ptr is that only one of them at a time can reference a particular entity.
I think I was assigning the traits of scoped_ptr to auto_ptr incorrectly and a little unfairly. My own bias is against auto_ptr because that transfer of ownership causes a side effect on the source object that is not normally associated with copying.
You aren't required to use pointers in C++ in many cases, so you may not need any kind of smart pointer:
struct Foo {
int bar;
int twice_bar()
{
return 2 * bar;
}
};
int twice_x(int x)
{
Foo f;
f.bar = x;
return f.twice_bar();
}