I came across the following code for a factory.
T::create(std::forward<Args>(args)...) returns a pointer to an object created dynamically. So basically if two objects have the same address then they are the same.
unique_ptr guarantees that a single unique_ptr container has ownership of the held pointer. This means that you can't make copies of a unique_ptr.
#pragma once
#include <memory>
#include <vector>
template <typename T>
class PoolFactory {
public:
template <typename... Args>
T *getInstance(Args... args) {
_createdItems.push_back(
std::unique_ptr<T>(T::create(std::forward<Args>(args)...)));
return _createdItems.back().get();
}
~PoolFactory() = default;
public:
std::vector<std::unique_ptr<T>> _createdItems;
};
Question
Suppose we are trying to insert an object that already exists in the vector. Since we are using a factory and IF the object already exists we just want to retrieve it. How will that archtitecture that contains move semantics guarantee that behavior?
Interpreting this code, in this factory your objects are identified by getInstance arguments. The interface also suggests that the callers know both T and its constructor arguments, so that they can construct T themselves.
The only use for this factory is to make each object a singleton.
You need a mapping (args...) -> object, rather than an array, so that first you look up an existing object using args... and create the object if it doesn't exist yet.
You've gotten the "uniqueness" of std::unique_ptr<T> backwards. There is not a magic mechanism such that when you create a std::unique_ptr<T> somehow it checks all the currently existing std::unique_ptr<T> and does something different if one owns the same pointer value.
Instead, it is assumed that you have previously newed a T * and construct a std::unique_ptr<T> from it, or call std::make_unique<T> to new a T and wrap it in a unique_ptr<T> for you. If that assumption is not the case, and the std::unique_ptr<T> is destroyed, your program's behaviour is undefined.
The prohibition on copying then assures you that delete is called exactly once, and often with no additional effort on your part (i.e. the pointee's lifetime is exactly the same as the pointer's lifetime)
Related
First take a look at what C++ Primer said about unique_ptr and shared_ptr:
$16.1.6. Efficiency and Flexibility
We can be certain that shared_ptr does not hold the deleter as a direct member, because the type of the deleter isn’t known until run time.
Because the type of the deleter is part of the type of a unique_ptr, the type of the deleter member is known at compile time. The deleter can be stored directly in each unique_ptr object.
So it seems like that the shared_ptr does not have a direct member of deleter, but unique_ptr does. However, the top-voted answer of another question says:
If you provide the deleter as template argument (as in unique_ptr) it is part of the type and you don't need to store anything additional in the objects of this type. If deleter is passed as constructor's argument (as in shared_ptr) you need to store it in the object. This is the cost of additional flexibility, since you can use different deleters for the objects of the same type.
The two quoted paragraph are totally conflicting, which makes me confused. What's more, many people says unique_ptr is zero overhead because it doesn't need to store the deleter as member. However, as we know, unique_ptr has a constructor of unique_ptr<obj,del> p(new obj,fcn), which means that we can pass a deleter to it, so unique_ptr seems to have stored deleter as a member. What a mess!
std::unique_ptr<T> is quite likely to be zero-overhead (with any sane standard-library implementation). std::unique_ptr<T, D>, for an arbitrary D, is not in general zero-overhead.
The reason is simple: Empty-Base Optimisation can be used to eliminate storage of the deleter in case it's an empty (and thus stateless) type (such as std::default_delete instantiations).
The key phrase which seems to confuse you is "The deleter can be stored directly". But there's no point in storing a deleter of type std::default_delete. If you need one, you can just create one as std::default_delete{}.
In general, stateless deleters do not need to be stored, as you can create them on demand.
Angew's answer explained pretty thoroughly what's going on.
For those curious how things could look under the covers
template<typename T, typename D, bool Empty = std::is_empty_v<D>>
class unique_ptr
{
T* ptr;
D d;
// ...
};
template<typename T, typename D>
class unique_ptr<T, D, true> : D
{
T* ptr;
// ...
};
Which specializes for empty deleters and take advantage of empty base optimization.
Brief intro:
unique_ptr can introduce some small overhead, but not because of the deleter, but because when you move from it value must be set to null where if you were using raw pointers you could leave the old pointer in bug prone but legitimate state where it still points to where it pointed before. Obviously smart optimizer can optimize, but it is not guaranteed.
Back to the deleter:
Other answers are correct, but elaborate. So here is the simplified version witout mention of EBO or other complicated terms.
If deleter is empty(has no state) you do not need to keep it inside the unique_ptr. If you need it you can just construct it when you need it. All you need to know is the deleter type(and that is one of the template arguments for unique_ptr).
For exaple consider following code, than also demonstrates simple creation on demand of a stateless object.
#include <iostream>
#include <string>
#include <string_view>
template<typename Person>
struct Greeter{
void greet(){
static_assert(std::is_empty_v<Person>, "Person must be stateless");
Person p; // Stateless Person instance constructed on demand
std::cout << "Hello " << p() << std::endl;
}
// ... and not kept as a member.
};
struct Bjarne{
std::string_view operator()(){
return "Bjarne";
}
};
int main() {
Greeter<Bjarne> hello_bjarne;
hello_bjarne.greet();
}
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?
Is there a reason behind why std::unique_ptr contains the deleter function signature as part of the template definition?
template<class T, class Deleter = std::default_delete<T>> class unique_ptr;
vs
template< class T > class shared_ptr;
The general objective of unique_ptr is to provide automatic deletion of a pointer when the unique-pointer goes out of scope. When the default deleter is used (just calls delete), there is no need for any extra data member in a unique_ptr object except for the pointer itself. This means that by default, unique_ptr has virtually no overhead whatsoever (since most, if not all, of its functions will be inlined).
But they also wanted to be able to provide the option to change the deleter, for the special circumstances where it makes sense. The only way to provide that option, while still being able to optimize it away (storage and inlining the calls) is to make it a static part of the type itself, i.e., through a template parameter. The point is that unique_ptr is intended to be a minimal overhead alternative for a raw pointer.
In the case of shared_ptr, the objective is quite different and the existing overhead is too. A shared-pointer actually uses a shared storage (dynamically allocated) where it stores the pointer, the reference count and the deleter object. In other words, there is already significant overhead and an appropriate place to put the deleter object without causing extra per-pointer overhead. Furthermore, with all the reference counting mechanisms, the overhead of a virtual call (to perform the deletion) pales in comparison to the existing overhead. That's why it was a natural choice to include that convenient feature of type-erasing the deleter object in the shared-pointers.
And, if you want to create a unique-pointer type that has a type-erased deleter, it is quite simple to do with a template alias:
template <typename T>
using any_unique_ptr = std::unique_ptr< T, std::function< void(T*) > >;
Or something similar along those lines, like with this deleter:
template <typename T>
struct type_erased_delete {
std::function< void(T*) > f;
type_erased_delete() : f(std::default_delete<T>()) { };
template <typename Func>
type_erased_delete(Func&& aF) : f(std::forward<Func>(aF)) { };
void operator()(T* p) const { f(p); };
};
template <typename T>
using any_unique_ptr = std::unique_ptr< T, type_erased_delete<T> >;
It means that, if the deleter has no state, the unique_ptr can be no larger than a plain pointer; and moving it no more expensive than copying and nulling a pointer.
To be able to handle multiple types of deleter, as shared_ptr can, it would need to use type erasure. To do that, each unique_ptr would need to contain a pointer to the deleter (to call it polymorphically), as well as a pointer to the managed object, doubling its size.
shared_ptr already has that extra overhead, since it needs a pointer to access the reference count. The deleter can be stored along with the reference count.
In the case of shared_ptr, it uses type-erasure on the deleter. There is some extra overhead in performing that type erasure, which unique_ptr does not want. In particular, most solutions will require allocation of dynamic memory for any function object deleter. Given the goal of unique_ptr, I can understand why they chose not to use that technique.
On the other hand, shared_ptr already includes some extra overhead for the reference counting, as it will already require dynamic allocation for the reference count. This means that the additional overhead for the type-erasure was smaller. That, and that's how boost::shared_ptr was written, and the standardized version was basically a capture of that.
Could be constructed with new[] - Therefore delete[] would need to be called - not delete.
The template gives you this option along with things such as logging etc.
I need to populate container with shared pointers and raw pointers at same time.
I guess shared_ptr<T> may be forced to behave like T*, if constructed with no-op deleter and no-op (de)allocator?
Or may be there is universal smart pointer, which binds to different (smart and raw) pointer types?
Or may be shared_ptr can point to one object, but manage lifetime of another (nullptr) object of same type?
Background. I have simple component system. Some components are built-in, no management required, raw pointers will be enough. Other components are external DLLs, they must be attached when requested and detached when removed from "session". For latter I am using wrapper component, that will detach DLL when destroyed.
EDIT: Background updated.
EDIT2: Question updated.
EDIT3: Found straight solution. See my answer if interested.
Generally speaking, no; a container contains exactly one type of object.
You could use some kind of boost::variant<shared_ptr<T>, T*> object as the contained object. But you'd need visitors to access the elements of it. Alternatively, you can give boost::shared_ptr a special deleter object that doesn't actually delete the pointed-to value.
Well, it really depends on what your needs are, and more specifically what you would like to achieve in terms of ownerships:
Should your vector own the shared_ptr<T> ? (or could you just store the pointee ?)
Should your vector own the T* ?
Then there are varied solutions.
Full ownership: just encapsulate the T* in a regular shared_ptr<T>
Partial ownership: as stated, you can just instantiate a shared_ptr<T> with a no-op deleter to wrap the T*
No ownership: just unwrap the shared_ptr<T> using .get() and only store T*
Shared pointer may point to one object, but manage lifetime of another object. Curiously, it may manage nothing, but still point to something. There is special constructor, that accepts another shared pointer, that used to determine which object to share. We can pass empty shared pointer in it.
template <typename T>
std::shared_ptr<T> fake_shared(T *ptr)
{
std::shared_ptr<T> dummy (nullptr); // we have nothing to manage;
std::shared_ptr<T> faked (dummy, ptr); // but we have something to point to;
return faked;
}
Moreover, we can exploit 2 facts. Firstly, this constructor will accept shared pointer of any (other) type. Secondly, std::shared_ptr<void> is legal, and may be used to express our intent more clearly.
template <typename T>
std::shared_ptr<T> fake_shared(T *ptr)
{
std::shared_ptr<void> dummy (nullptr); // we have really nothing to manage;
std::shared_ptr<T> faked (dummy, ptr); // but we have something to point to;
return faked;
}
Alternatively, one may use constructor, that accepts custom deleter, and pass no-op deleter into it.
template <typename T>
std::shared_ptr<T> fake_shared(T *ptr)
{
std::shared_ptr<T> faked (ptr, [](T *){}); // won't be deleted;
return faked;
}
EDIT: Revision, shared void, custom deleter.
Is it possible to write a smart pointer which allocates the object itself in its constructor - instead of the developer having to call new? In other words, instead of writing:
std::unique_ptr<myClass> my_ptr(new myClass(arg1, arg2))
...one could write:
std::smarter_ptr<myClass> my_ptr(arg1, arg2)
Is the language syntax capable of expressing this? Would this be desirable? Hideous? I'm thinking in particular of protecting against this mistake (which I've made myself, of course):
myFunction(std::unique_ptr<myClass>(new myClass()), std::unique_ptr<myClass>(new myClass()))
...which risks leaking whichever object is allocated first if the second allocation happens and throws before the first object is safely ensconced in its smart pointer. But would a smarter pointer actually make this safe?
Look at the implementation of make_shared(). It does this allocates a new object and creates a shared_ptr out of it.
In general, this can't be done with a smart pointer's constructor; there would be an ambiguity over whether a pointer argument should be used to initalise the smart pointer, or forwarded to create a new object.
It can be done with a factory function, for example:
template <typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
If you're using std::shared_ptr, then you can use std::make_shared. This also gives the advantage of only requiring one memory allocation, where std::shared_ptr<T>(new T) will require one for the object and a second for the shared reference count.
It's essentially the same problem that necessitate std::find and std::find_if. You can't distinguish this ctor from the existing ctors of shared_ptr in the new myClass(arg1) case. The number of arguments is equal, and arg1 can have any type.
Therefore, you need another name, and that's make_shared
This is newly possible with C++11 which added perfect forwarding and variadic templates.