This got marked as a duplicate because it was claimed that it is already answered here. I don't feel that my question is answered at all there since that post doesn't deal with vectors or similar containers at all. None of the comments on my question answer it either. So, on the theory that I buried my question too deep I'll put it right up front before my example.
When we invoke .clear() on a vector it calls the destructor on each element of the vector. The "well known" exception is when it is a vector of pointers. My example below suggests that when we .clear() a vector of vectors the destructor apparently doesn't get called on the vectors that are its elements, since I can still access them afterwards. Why? In what way is a vector "kind of like" a pointer so that the destructor does not get called on it?
To be clear about a few things:
I know a vector is not a pointer. But I'm rather unclear about what the "guts" of a vector really look like, which is probably why I don't understand this.
Also, I know this is undefined behaviour. My question is, why is it undefined? I would have hoped that this would have led to explicit errors, not undefined behaviour. I generally hope that when I do something stupid the programming language should kick me in the butt. Here C++ is failing to punish me for doing something very dumb, and I'm curious about why. (Yes, I know the makers of the C++ standard can't predict every foolish thing a programmer like me might do, and it is our responsibility to not be foolish. But the continued accessibility of these vectors after a
.clear()
is pretty counterintuitive.)
It came up because of unexpected behaviour in a very long program and I traced the problem to this. I fixed it, but am still puzzled by why that program failed subtly rather than spectacularly.
OK, here's the original question:
There are a couple of other questions here related to this such as this one and this one but I don't think they quite address the issue I'm wondering about. My understanding is that calling clear on a vector calls the destructor on each element of the vector (but see below for an exception to this). So, with a vector of vectors I would have assumed that the destructor would be called on each vector within the vector of vectors. That appears to be incorrect. I know several ways to write my code to deal with it. But I'm still a bit puzzled by the behaviour. This code shows the behaviour:
using namespace std;
#include <iostream>
#include <vector>
void writeMat(vector<vector<double>> &vec);
int main()
{
vector<vector<double>> b;
writeMat(b);
// Trying to get b[0].size() here results in a core dump, as expected.
vector<double> btry1 = {1.0, 2.0};
vector<double> btry2 = {3.0, 4.0};
b.push_back(btry1);
b.push_back(btry2);
cout << "Added some stuff to the vector." << endl;
writeMat(b);
b.clear();
cout << "Cleared the vector. Now it is: " << endl;
writeMat(b);
cout << "But final check... b.size() = " << b.size() << " and b[0].size() = " << b[0].size() << endl;
return 0;
}
void writeMat(vector<vector<double>> &vec)
{
cout << "vec.size() = " << vec.size() << endl;
for (int i = 0; i < vec.size(); i++)
{
cout << "vec[" << i << "].size() = " << (vec[0]).size() << endl;
}
cout << "vec is: " << endl << "--------------------" << endl;
for (int i = 0; i < vec.size(); i++)
{
for (int j = 0; j < vec[i].size(); j++)
{
cout << vec[i][j] << " ";
}
cout << endl;
}
cout << "--------------------" << endl;
}
Generates output:
vec.size() = 0
vec is:
--------------------
--------------------
Added some stuff to the vector.
vec.size() = 2
vec[0].size() = 2
vec[1].size() = 2
vec is:
--------------------
1 2
3 4
--------------------
Cleared the vector. Now it is:
vec.size() = 0
vec is:
--------------------
--------------------
But final check... b.size() = 0 and b[0].size() = 2
I would expect that calling b[0].size()
after calling b.clear()
would result in a core dump just like it would if I called it before any elements had been added to b. But instead it returns the value 2, which suggests that although b.size()
is now zero, b[0]
is still hanging around in memory. Indeed, modifying the above code I find that after b.clear()
, it will still report, for example, that b[0][1]
is 2.
I see here that for a vector like vector<MyClass>
, the destructor gets called on each element of the vector when clear()
is called but for a vector of pointers like vector<MyClass*>
it does not. So I guess a vector is "more similar" to a pointer in some way than it is to an object. It has never been very clear to me what containers like vector are in relation to more primitive types. I think of a vector as as an old style array (not std::array, an old array like we would have used in C, 20 years ago...) with a rather thin wrapper of methods to make it friendlier. But that can't be quite right because passing a pointer to a vector has a very different effect from passing a pointer to an array.
So, I guess my question is roughly this: A vector is not a pointer. But when clearing a vector of vectors the behaviour seems to be similar to clearing a vector of pointers. So what is the similarity that causes this behaviour? I realize that this is likely to get into the nitty gritty of what the block of memory set aside for a vector will look like as opposed to what the block of memory set aside for an array would look like.
It occurs to me that this might be specific to the implementation of C++. I'm using g++ in a Linux OS.