I got a bit confused lately about the memory (de)allocation of std::vectors
Lets assume I got normal vector of integer:
std::vector<int> intv;
When I push_back
some int
's it grows by time. And when I leave the scope (i.e.) of the function, it gets deallocated without the need of extra calls.
Great. Lets have another example:
struct foo_t{
std::string bar:
unsigned int derp;
}
void hurr(){
std::vector<foo_t> foov;
foo_t foo;
foo.bar = "Sup?";
foo.derp = 1337;
foov.push_back(foo);
}
Okay. When I call hurr()
the vector gets created, a foo_t
instance gets created, the instance gets filled and pushed to the vector. So when I leave the function, the vector gets deallocated and the content (here one foo_t
) gets deallocated, too?
Next example:
struct foo_t{
std::string bar:
unsigned int derp;
}
std::vector<foo_t> hurr(){
std::vector<foo_t> foov;
foo_t foo;
foo.bar = "Sup?";
foo.derp = 1337;
foov.push_back(foo);
return foov;
}
In my understanding, the vector and its contents live in the stack, which gets (eventually) overwritten by time and the vector I have returned and its contents will be useless. Or does it actually returns a copy of the vector with a copy of its contents (requires a Copy-Constructor for the content datatype if its not a POD)?
And something obvious:
struct foo_t{
std::string bar:
unsigned int derp;
}
std::vector<foo_t*> hurr(){
std::vector<foo_t*> foov;
foo_t foo = new foo_t;
foo->bar = "Sup?";
foo->derp = 1337;
foov.push_back(foo);
return foov;
}
Now I have to manually iterate over the vector, delete its contents and then I can safely let the vector fall out of scope, right?
Vectors are assigned memory in blocks of contiguous locations. When the memory allocated for the vector falls short of storing new elements, a new memory block is allocated to vector and all elements are copied from the old location to the new location.
std::vector does not cause memory leaks, careless programmers do. You should also include an example that actually exhibits the behavior you are experiencing, including calls to the CRT debug API. There's a good possibility that you're incorrectly interpreting the leaks based on when they are reported.
Vector are implemented as dynamic arrays with list interface whereas arrays can be implemented as statically or dynamically with primitive data type interface. Size of arrays are fixed whereas the vectors are resizable i.e they can grow and shrink as vectors are allocated on heap memory.
Yes, the elements of a std::vector are guaranteed to be contiguous.
This example:
struct foo_t{
std::string bar;
unsigned int derp;
};
void hurr(){
std::vector<foo_t> foov;
foo_t foo;
foo.bar = "Sup?";
foo.derp = 1337;
foov.push_back(foo);
}
After hurv()
finished, foov
and foo
are both freed.
std::vector<foo_t> hurr(){
std::vector<foo_t> foov;
foo_t foo;
foo.bar = "Sup?";
foo.derp = 1337;
foov.push_back(foo);
return foov;
}
the result std::vector<foo_t>
of hurr()
is valid with 1 foo_t
in it and it's valid.
The return foov;
may call a copy contructor of std::vector<foo_t>
, and it have its free to not make that copy, see copy elision
Anyway, from C++11, you can write this:
struct foo_t{
std::string bar;
unsigned int derp;
// we will copy the string anyway, pass-by-value
foo_t(std::string bar_, unsigned int d_)
: bar(std::move(bar_)), derp(d_) {}
};
std::vector<foo_t> hurr(){
std::vector<foo_t> foov;
// This is better, in place construction, no temporary
foov.emplace_back("Sup?", 1337);
// This require a temporary
foov.push_back(foo_t{"Sup?", 1337});
return foov;
}
And, for the last example, yes, you have to manually itterate over the vector, delete its contents and then I can safely let the vector fall out of scope when you no longer want to use the result of hurr()
, (not in hurr()
)
foov.push_back(foo);
Actually, you constructed a foo_v
and you pushed it back, which actually created a new foo_v
and called the copy constructor with foov
as a paremeter. Use emplace_back
if you want to avoid this.
return foov;
The compiler may optimize this using return value optimization. See this short program I've made running on coliru as an example. Refer to the other excellent answers in this question.
std::vector<foo_t*> foov;
/* add elements to foov with new */
Now I have to manually itterate over the vector, delete its contents and then I can safely let the vector fall out of scope, right?
Yes, you do. For the same reasons
int* a = new int();
Will not delete a;
when a
dies.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With