How does std::vector allocate objects? It would seem as if it just uses std::allocator::allocate to create a block of memory, but then never calls std::allocate::construct. Is this true? Does std::vector only allocate memory and never construct the objects as memory allocation?
What if there is no default constructor? How is the constructor called when there is no default constructor on the object? What if there is more than one parameter?
For example, with this code there is no default constructor and std::allocator allows it.
#include <vector>
using namespace std;
class A{
protected:
int m;
public:
explicit A(int a) : m(a) { }
};
int main(){
vector<A> test;
return 0;
}
This has rather changed since C++11.
In C++03, construct could only perform copy-construction in-place.
However, note that std::vector in particular is an array of objects, but there is a distinct size and capacity. That is, there can be more empty elements beyond the end of the part of the array that contains useful data.
That is why the standard library's allocator has a separation between "construction" and "memory allocation." The allocator does do both, but not at the same time. This allows std::vector to allocate more memory than it uses. When you add new elements, it doesn't necessarily have to allocate more memory; it can just use the spare memory it has left over via a call to allocator::construct.
Also, note that all of the C++03 functions that add elements to std::vector take an element as a parameter. push_back, insert, even the sized constructor takes as a value as an argument. Yes, it's a default parameter, but it still takes a value as an element. This element is copied into the vector, using a call to the allocator's construct method which takes a copy.
In C++11, standard containers are required to use the allocator_traits<>::construct function. This is a varadic function that forwards its parameters to the actual construct. This traits function will (by default. It can be specialized) call the allocator::construct method if that call is well-formed. If it isn't, it will try placement new.
This allows for the new emplace functions to work.
But yes, the objects contained in standard library containers are in fact constructed objects. Even if the allocator's construct method is not called.
It's implementation dependent, but a typical implementation uses std::allocator::allocate to allocate the block of memory, and then uses the placement new to construct, via the copy constructor (or move constructor in C++11), the instances.
When elements are erased, their destructors are directly invoked to destroy the objects, even if the underlying memory isn't released.
Related
If you want to have an array as a member variable of a class there are two main options:
A: Allocate the memory on the heap
class X
{
int * arr;
public:
UnionFind(int numNodes)
{
arr = new int[numNodes];
}
}
B: Use a vector
class X
{
vector <int> arr;
public:
UnionFind(int numNodes)
{
arr.resize(numNodes);
}
}
Which of these is the preferred method? I know one drawback of heap allocated arrays is that you need to take care of deleting the memory yourself.
As a small side question, if an object of X is created on the heap is vector <int> arr also in the heap within the object? If so, how come vector <int> arr does not manually need to be deleted?
When you have the choice between a dynamically allocated C-style array and a std::vector<>, choose the vector.
It is safe, does all the alloc/realloc/resizing for you
It makes you code more flexible, readable, and easier to maintain
It is extremely efficient in most use cases
It provides explicit iterators, and plenty of member functions, including size()
Many implementations will do index checking in debug mode to catch out-of-bounds errors
Note that std::array exists for most of the cases where a C-array would be preferable (e.g., when allocation on the stack is preferred)
You should prefer vector:
the vector and vector's elements' destructors are guaranteed to run at the appropriate times
things like .push_back are massively easier and more concise to use correctly than coding your own checks on "capacity" and resizing/copy-constructing/moving in a C++ object-aware fashion
it's easier to apply algorithms to Standard containers, use the iterators etc.
it will be easier to move from vector to another Standard container (list, stack, map, unordered_map, deque etc) if evolving application/library needs suggest it
vector has some housekeeping information that's useful: size(), capacity()
before C++11 there was a single performance issue compared to using new[] and delete[] - you couldn't do an up-front "sizing" of the vector to hold however-many elements without copy-constructing their values from a prototypical element (constructor "2" here, and resize here) - that meant the constructor or resize had to iterate over every element doing copy construction even if the default constructor was a no-op (e.g. deliberately leaving all members uninitialised)
this is very rarely relevant or problematic, and indeed the C++ behaviour was generally safer
because it's a proper class, you can (whether you should is another matter) overload operator<<, operator>> for conveniently streaming arbitrary vectors
if an object of X is created on the heap is vector <int> arr also in the heap within the object? If so, how come vector <int> arr does not manually need to be deleted?
Yes, the vector object itself will be embedded within X, so will be on the heap too (similarly, it could be embedded in an automatic/stack variable, a global/namespace/static variable, a thread-specific variable etc.). That said, the vector object contains a pointer which tracks any further memory needed for elements in the vector, and that memory is by default dynamically allocated (i.e. on the heap) regardless of where the vector object itself is held.
Member variables with destructors (such as any vector) have them called automatically by the containing class's destructor.
If I have a var
vector<myClass> myVector;
Is it already initialized?, ie, may I add (push_back) inmediately elements or I should call the constructor in the following way?
myVector = vector<myClass>();
On the other hand, is it the same std::vector<myClass> and vector<myClass>?
Is it already initialized?
Yes (assuming this is std::vector). As with any sane class, all its constructors (including the default constructor used here) put it into a well-defined state. At this point, it's a valid, empty vector.
I should call the constructor in the following way?
That's not calling the constructor (at least, not on myVector). Constructors are called automatically during object initialisation; there's no way to call it a second time. This creates another empty vector, then copy-assigns it to myVector.
On the other hand, is it the same std::vector and vector?
Presumably, this is std::vector, dumped into the global namespace with an evil using namespace std;. To avoid doubt, confusion and potential amibiguity, you should avoid doing that, remove any rogue using-directives, and always refer to it as std::vector.
In this case
vector<myClass> myVector;
there is no need to call default constructor separately.
You can call push_back and other methods.
The notation
MyClassType a;
actually calls MyClassType's default constructor, if it has one. So yes, a vector is already initialized and ready to use.
Your second snippet:
myVector = vector<myClass>();
Actually creates a new, temporary vector, which is default constructed, and then calls myVector's copy assignment operator operator=().
In this regard, C++ is different from many other languages. For example, in Java, you'd need to do MyClassType a = new MyClassType(). This is not necessary in C++. Whenever you declare a value of class type with automatic storage1, the object is automatically default constructed. This is also true for class members. Let's say you have:
class A {
std::vector<int> m_myVector;
};
Then there is no need to initialize m_myVector - it's done automatically whenever you instantiate an object of class A.
It's different when you allocate objects on the heap:
// Note: DON'T use raw pointers in actual code - use smart pointers instead.
// Just for illustration purposes.
// This is just a pointer to an A, it's not yet initialized.
A* myA;
myA = new A();
// now myA points to a heap allocated, default constructed A object.
// Note that you can omit the default constructor's `()` and just write:
myA = new A;
Heap allocating an object is actually closer to what Java does under the hood. Anyway, when writing proper C++, you rarely need to heap allocate, and you wouldn't ever do it with a vector.
1 Automatic storage: Put simply, anything you create in C++ without using new/delete or similar.
I am writing a template class which internally manages an array of the given type. Like this:
template<typename T>
class Example {
// ...
private:
T* objects; // allocated in c'tor (array), deleted in d'tor
// ...
};
I was wondering if C++ calls the destructor of each object in objects when I delete it via delete[] objects;.
I need to know this, because the objects in my class do not contain sensible values all the time, so the destructors should not be called when they don't.
Additionally, I'd like to know if the destructors would be called if I declared a fixed-sized array like T objects[100] as part of Example<T>.
If T has a destructor then it will be invoked by delete[]. From section 5.3.5 Delete of the c++11 standard (draft n3337), clause 6:
If the value of the operand of the delete-expression is not a null pointer value, the delete-expression will
invoke the destructor (if any) for the object or the elements of the array being deleted. In the case of an
array, the elements will be destroyed in order of decreasing address (that is, in reverse order of the completion
of their constructor; see 12.6.2).
The destructor for a type T will also be invoked for each element in an array of T[] when the array is not dynamically allocated and array goes out of scope (lifetime ends).
I need to know this, because the objects in my class do not contain sensible values all the time, so the destructors should not be called when they don't.
But, there seems to be a very significant problem with an object that can acquire a state where it cannot be destructed.
Yes, the destructor will be called for all objects in the array when using delete[]. But that shouldn't be an issue, since the constructor was called for all objects in the array when you used new[] (you did, right ?) to allocate it.
If a constructed object can be in such a state that calling the destructor would be invalid, then there's something seriously wrong with your object. You need to make your destructor work in all cases.
delete [] does call the destructor for each element of the array. Same happens for a member array (your T objects[100]).
You want to keep it as pointer, and design the destructor (and copy constructor, and copy assignment operator, see rule of three/five) for your template to deal with "non-sensible" values pointed to by objects.
The answer is yes. Destructor for each object is called.
On a related note, you should try to avoid using delete whenever possible. Use smart pointers (e.g.,unique_ptr, shared_ptr) and STL containers (e.g., std::vector, std::array) instead.
delete[] objects is similar (but not identical) to:
for (i = 0; i < num_of_objects; ++i) {
delete objects[i];
}
since delete calls the destructor, you can expect delete[] to do the same.
Yes, delete[] guarantees destructors are called on every object.
Depending on your use case, using Boost pointer containers, or simply containers of smart pointers, might make it a lot easier to have (exception safe) collection of pointers.
Hello I have this question:
I would like to have a vector as class member. This is perhaps my question
easier for you and I apologize for that.
how should I declare the vector? And is this correct? std::vector<int> *myVector; or std::vector<int> myVector ?
how should I handle this vector in dealloc?
How can I initialize the array into a if?
Is this correct?
if(myCondition)
{
if(!myVector) //is this correct?
myVector = new std::vector<int>(); //is this correct? on this i have a error
}
You most certainly want to use std::vector<int> myVector. No need to initialize it, as it gets automatically initialized in the constructor of your class and deallocated when your class is destroyed.
Just use automatic allocation: declare it as a member like this:
class YourClass
{
std::vector<int> myVector;
// ...
};
The array gets constructed automatically before any of your constructor is run and is destroyed automatically when your object is deallocated, you don't need to care about it (also, the default copy constructor and assignment operator will handle copying gracefully automatically).
If, instead, you want to create the array only after a particular condition, you have to resort to a (smart) pointer and dynamic allocation, but IMHO it's quite cumbersome (especially because you then have to get right the "big three" - copy constructor, assignment operator, destructor); you could instead simply allocate the vector with automatic allocation and use a separate flag to mark your array as not initialized, or just check if its size is 0.
That depends entirely on context - what the vector means and why you need it. Should it be shared among multiple objects? If you don't know, don't keep a pointer, go with your second option.
std::vector<int> myVector;
If you have strong reasons to have a pointer, then please use a smart pointer, the one that provides most appropriate ownership for your situation - shared_ptr, scoped_ptr, unique_ptr or whatever_ptr
Most of the time, when we use standard library, We do not need to care about the memory allocation/deallocation. The template will handle it automatically. eg. The memory of a std::vector will be increase or decrease according to the elements stored in this vector. This would be an example.
Therefore, almost you can use it this way in your case.
std::vector<int> myVector //your second declaration
if(myCondition)
{
myVector.push(some_int); // use it directly
}
The memory the vector used will be deallocated when the Class object you created is destroyed.
The folowing constructor
std::vector<Object> objects(n);
creates n objects calling the default constructor, i.e. something like that:
std::vector <Object> objects;
for (unsigned int i = 0; i < n; i++) objects.push_back(o);
Is this procedure also valid for dynamically allocated objects? Does the construction
std::vector<Object *> objects(n);
represent this functionality?
std::vector <Object*> objects;
for (unsigned int i = 0; i < n; i++) objects.push_back(new Object());
If not, is there a way how to arrange it?
std::vector<Object> objects(n);
The behavior of this depends on which version of the C++ Standard your Standard Library implementation implements:
In C++03, this creates one default constructed Object and then copy constructs that object n times.
In C++0x, this default constructs n Objects.
The difference shouldn't usually matter, but it's good to know.
std::vector<Object *> objects(n);
This creates a vector with n null Object*s in it. Since Object* is not a class type and does not have a constructor, the newly inserted objects are value initialized, which for pointers means they are set to NULL.
If you want to dynamically create new objects and then store pointers to them in the container, you need to call new yourself. Note that you should not be storing raw pointers in a standard library container if the container owns the pointed-to objects. Doing so is not exception safe.
You should be using a smart pointer like shared_ptr or unique_ptr instead (note: the auto_ptr smart pointer cannot be stored in containers due to its unusual copy semantics, thus shared_ptr or unique_ptr should be used).
In any case, to insert pointers to n distinct, dynamically allocated objects into the container, you need to call new n times to create those n objects. There's nothing wrong with your for loop solution.
The folowing constructor
std::vector<Object> objects(n);
creates n objects calling the default constructor
Yes, but the default constructor is used only to construct the second optional parameter to the constructor of vector, the n objects in the vector are constructed by copying this parameter. [C++03 answer]
If you did something like:
std::vector<Object*> objects(n, new Object());
you would dynamically allocate one object and have n pointers to that object in your vector which is probably not what you want.
It is almost always a bad idea to use a container of pointers if that container is supposed to own the dynamically allocated objects. You should consider something like boost::ptr_vector, or if that is not possible a container of smart pointers (not std::auto_ptr, though).
No, the vector won't be automatically created with pointers to Object instances. You will have to perform the for loop you have written in order to populate it correctly.
You will also need to delete each of these objects when you have finished with them too.
Your final code example has the right general idea, but tread carefully: vector will not manage the allocations for you if you do that! objects.clear() will leak memory, for instance.
You probably want to use std::vector<some_smart_ptr<Object> > instead, but choosing the right smart pointer class requires care and attention to (for instance) what happens when you copy elements from one vector to another. boost::shared_ptr is a safe choice, but may have unnecessary overhead for your use case. boost::ptr_vector may be better.