I'd like to have something equivalent to a C++ std::vector, where the underlying objects are immutable. So I can push_back() items to add them to the vector, etc. An actual std::vector maintains an array underneath that's bigger than the size of the vector, filled with default constructed objects, and when you push_back(), it does an assignment to an element in the array. My immutable objects don't have a default constructor, and assignment is a mutating operation, so that's out too.
I can do a vector<boost::optional<T>>, but that's a messy interface because I only want to put validly constructed objects into the vector, and only get those out of the vector.
I thought boost had something like this, but I couldn't find it. Does something like this exist?
Your conception of how the vector works is incorrect.
The vector uses the allocator to allocate raw memory. That raw memory does not contain default constructed objects--it's just raw memory.
When you do a push_back (for example) it then uses a placement new to construct an object into the raw memory. Likewise, when you erase an object, it will end up directly invoking its destructor to turn the object back into raw memory.
With a current (C++11 or later) implementation of std::vector, your object doesn't need to support default construction or assignment. Supporting move construction and move assignment should be sufficient. To put them to use, you'd want to use emplace_back instead of push_back though.
Related
What I have readen say that a common approach to make a vector of pointer that own the pointers, of MyObject for example for simples uses, is vector<unique_pointer<MyObject>>.
But each time we access an element will call unique_ptr::get(). There is also a little overhead.
Why isn't vector of the pointer with "custom deleter", if such a thing exists (I don't have used allocators), more standard? That is, a smart vector instead of a vector of a smart pointer. It will eliminate the little overhead of using unique_ptr::get().
Something like vector<MyObject*, delete_on_destroy_allocator<MyObject>> or unique_vector<MyObject>.
The vector would take the behaviour "delete pointer when destroy" instead of duplicate this behaviour in each unique_ptr , is there a reason, or is just the overhead neglegible ?
Why isn't vector of pointer with "custom deleter", if such a thing exists
Because such a thing doesn't exist and cannot exist.
The allocator supplied to a container exists to allocate memory for the container and (optionally) creates/destroys the objects in that container. A vector<T*> is a container of pointers; therefore, the allocator allocates memory for the pointer and (optionally) creates/destroys the pointers. It is not responsible for the content of the pointer: the object it points to. That is the domain of the user to provide and manage.
If an allocator takes responsibility for destroying the object being pointed to, then it must logically also have responsibility for creating the object being pointed to, yes? After all, if it didn't, and we copied such a vector<T*, owning_allocator>, each copy would expect to destroy the objects being pointed to. But since they're pointing to the same objects (copying a vector<T> copies the Ts), you get a double destroy.
Therefore, if owning_allocator::destruct is going to delete the memory, owning_allocator::construct must also create the object being pointed to.
So... what does this do:
vector<T*, owning_allocator> vec;
vec.push_back(new T());
See the problem? allocator::construct cannot decide when to create a T and when not to. It doesn't know if its being called because of a vector copy operation or because push_back is being called with a user-created T*. All it knows is that it is being called with a T* value (technically a reference to a T*, but that's irrelevant, since it will be called with such a reference in both cases).
Therefore, either it 1) allocates a new object (initialized via a copy from the pointer it is given), or 2) it copies the pointer value. And since it cannot detect which situation is in play, it must always pick the same option. If it does #1, then the above code is a memory leak, because the vector didn't store the new T(), and nobody else deleted it. If it does #2, then you can't copy such a vector (and the story for internal vector reallocation is equally hazy).
What you want is not possible.
A vector<T> is a container of Ts, whatever T may be. It treats T as whatever it is; any meaning of this value is up to the user. And ownership semantics are part of that meaning.
T* has no ownership semantics, so vector<T*> also has no ownership semantics. unique_ptr<T> has ownership semantics, so vector<unique_ptr<T>> also has ownership semantics.
This is why Boost has ptr_vector<T>, which is explicitly a vector-style class that specifically contains pointers to Ts. It has a slightly modified interface because of this; if you hand it a T*, it knows it is adopting the T* and will destroy it. If you hand it a T, then it allocates a new T and copies/moves the value into the newly allocated T. This is a different container, with a different interface, and different behavior; therefore, it merits a different type from vector<T*>.
Neither a vector of unique_ptr's nor a vector of plain pointers are the preferred way to store data. In your example: std::vector<MyObject> is usually just fine, and if you know the size at compile time, try std::array<int>.
If you absolutely need indirect references , you can also consider std::vector<std::reference_wrapper<MyObject>>. Read about reference wrappers here.
Having said that... if you:
Need to store your vector somewhere else than your actual data, or
If MyObjects are very large / expensive to move, or
If construction or destruction of MyObjects has real-world side-effects which you want to avoid;
and, additionally, you want your MyObject to be freed when it's no longer refered to from the vector is gone - the vector of unique pointers is relevant.
Now, pointers are just a plain and simple data type inherited from the C language; it doesn't have custom deleters or custom anything... but - std::unique_ptr does support custom deleters. Also, it may be the case that you have more complex resource management needs for which it doesn't makes sense to have each element manage its own allocation and de-allocation - in which case as "smart" vector class may be relevant.
So: Different data structures fit different scenarios.
I was trying to write user defined object with std::vector. I read that for User Defined classes, if Copy Constructor and Assignment Operator are public then only one can insert its object in STL container. This is because of two reasons::
All STL contains always stores the copy of inserted objects, not the actual one. So, whenever we insert any element or object in container then it’s copy constructor is called to create a copy and then this copy is inserted into the container.
While insertion in std::vector it might be possible that storage relocation takes place internally due to insufficient space. In such cases assignment operator will be called on objects inside the container to copy them from one location to another. why all STL container always stores the copy of inserted objects, not the actual one? I couldn't understand the reason as to why they didn't allow the storing of the actual object. what was the disadvantage?
The standard containers in C++ allocate memory that they manage. If your program creates an object, then that object is in another memory place, so to be part of the container, a copy is made in the memory of the container.
Instead of copying, moving could have been done, but in a lot of cases, that would not be more efficient and sometimes it could even be quite inconvenient.
A good solution to avoid copying is to create the object directly in the container with the emplace-functions.
About the vector growing, because it is possible that the new vector has to be at a different memory address and the memory contains the objects, they have to be moved or copied. This answer shows how you can make the vector move upon resizing.
Consider as an example the unique_ptr and its release method that returns a pointer to the managed object and releases the ownership.
Is there any way to release the ownership of the underlying array of a std::array?
Ok, one could use a std::unique_ptr instead of a std::array and that's all. Anyway, the latter has a few nice features like the size member method that are useful sometimes.
Is there any way to release the ownership of the underlying array of a std::array?
No. A std::array is just a wrapper for a raw array. It can be reassigned but that is actually a copy operation of all the elements in the array. The destination array does not point to the source array.
You should also note that a std::array and a std::unique_ptr<type[]> are different in that the std::array size must be know at compile time where the std::unique_ptr<type[]> size can be set at run time. All std::unique_ptr<type[]> really does is wrap a type * name = new type[some_size].
Nope, an std::array is just a simple wrapper around a native array, so it is on the stack and cannot release its contents unless it goes out of scope when the contents are automatically popped from the stack
You should consider using a std::vector instead. Since you are already dealing with an array on the heap. You can then std::move the vector into another one to "transfer" ownership of the contents. For example
another_vec = std::move(old_vec); // now another_vec has the contents
Note If you use a unique_ptr the array you are getting is on the heap and not on the stack! So you might be better off using a std::vector and its data() function instead. But I am not completely sure of your use case.
Another note Another thing that is not that obvious when thinking about using an std::array is that the type is a heavyweight object, this means that the regular rvalue optimizations might not work as optimally since it is not as trivial to move as a vector
«releasing» a std::array basically means calling the destructor and using the memory for something else.
You can explicitly destroy an explicitly constructed std::array by using the std::*::destroy functionality found in the standard library or calling the destructor explicitly.
This is something you usually want to avoid, unless you are implementing very basic data structures where you have no other choice. One use case is if you want to control when and how you construct and destruct a global array without an indirection through a pointer.
C++17 may provide the new functions
destroy_at, destroy and destroy_n to explicitly destruct objects.
See also the C++ FAQ on destructors.
I have an object called LastQueryInfo lastQuery in my class. Every time this object changes, I add it to a vector called history.
Initially, when I did history.push_back(lastQuery) I didn't know what would happen - is the vector going to make a copy of the object? or is it going to keep a reference to it? So if later I modify lastQuery, are all the objects (assuming they are references) in the history vector going to be modified?
After some testing, I found that history.push_back(lastQuery) is indeed going to make a copy of the object, then add it to the vector. But how can I know that without doing any tests? How can I know when C++ is going to make a copy, and when it's going to add the actual object?
std::vector always stores a copy† of whatever you push_back(). So modifying the value you passed in will not affect the value stored in the vector. It isn't like Java or C# where an Object o; is actually a reference to the object, and the object lives until the garbage collector comes and picks it up when the last reference to it goes away. In C++, Object o; is the actual object, which will go away at the end of its scope.
So if std::vector only stores references to the objects you push_back(), then it will be utterly useless for things like this:
std::vector<int> numbers;
for(/* loop condition */) {
int number = GetUserInput();
numbers.push_back(n);
}
Since number will go away at the end of the loop, numbers would hold references to something that will not exist if std::vector was implemented by storing only references. If std::vector actually stored the values, you could access them even after the loop.
† C++11 supports move semantics, so if the thing you're pushing back is actually a temporary that will go away soon, it'll move the internals of the object into the vector storage instead of copying. You can also explicitly use C++11's std::move() to "force" the move during push_back(). But vector will copy the value in every other case. It's an implementation detail to optimize the performance of vectors.
Using push_back will always create a copy of the object that is being stored.
If you are using c++11, then there are two ways to avoid the copy :
use the emplace method
move the created object into the vector: vec.push_back( std::move( obj ) );
If you are not using c++11, then the only thing you can do is to use pointers or boost::shared_ptr as vector types.
Ok, I'm using C++ STL containers (currently vector<customType*>).
Now I need to remove elements from the container,
but using erase deconstructs the object, which is bad, since I'm taking it off one, and putting it onto a variable doing some processing then onto another one.
At the moment my code is quite nasty, and I'm just putting NULL in its place after I've read it, into my variable, and then putting a if (Q[ii]NULL) continue.
But this is not so great.
If you have a container of pointers (which it sounds like you do since you are assigning NULL to "erased" elements), then erasing an element from the container does not delete the pointed-to object. You are responsible for doing that yourself.
If you have a container of objects (well, non-pointer objects), then you need to copy the element out of the container before you erase it.
You can't really remove the element from the vector without destroying it. If your vector stores pointers, you can remove the pointer to the element, which won't actually destroy the element itself.
STL container operations have copy semantics. So any time you add or remove elements, the constructor or destructor will get called accordingly (assuming a non-primitive type). And if the vector gets resized in the process, all objects will be copy-constructed and the originals destructed. There's no way to avoid all this copying.
The only way to avoid the overhead is to have a vector of (smart) pointers instead of objects.