Adding vector to 2D vector, and keeping reference to the last vector - c++

I am writing a program and there was a very subtle error that was hard to recognize.
I examined the program for hours and it looks like it's from my misuse of resize() in allocating a new vector in 2D vector, from my misuse of back() in vector, or from updating a reference variable.
I wrote a simpler code that contains similar problem in my original program for demonstration:
int main() {
vector<vector<int>> A(1);
vector<int> &recent = A.back();
recent.emplace_back(50);
cout << A.back()[0] << endl; //prints 50
A.resize(2);
A[1] = vector<int>();
cout << A.size() << endl; //prints 2
recent = A.back();
cout << A[1].size() << endl; //prints 0
recent.emplace_back(20); //Segmentation fault occurs!!
cout << A[1][0] << endl;
}
Segmentation fault occurs when I tried to emplace_back(20), although in my original program it doesn't throw any error and doesn't emplace the element either.
Possible cause of problem, in my opinion is:
I used resize() to allocate a new vector after the current last position of the 2D vector A, because I didn't know how to emplace_back() an empty vector.
2, 3. In recent = A.back();, I'm not sure if I am updating the reference variable(defined as vector<int> &recent) correctly, and if back() gives the correct reference to the newly allocated vector at the end of the 2D vector A.
The logic looked perfectly fine, but obviously I am doing something wrong.
What am I doing wrong, and what can I do instead?

References in C++ cannot be "updated". The call to resize may (and likely will) invalidate any reference to the original content of the vector. Thus recent is a dangling reference after A.resize(2);.
When creating the initial A here
std::vector<std::vector<int>> A(1);
the outer vector is required to be able to store one single vector.
If you add another std::vector<int> to A the first element of A is likely to move to another memory location. Since recent will always refer to the old memory location you see the segfault.
See 'c++ Vector, what happens whenever it expands/reallocate on stack?' for how vectors work.
On the question how to circumvent this:
If you know the size of the vector in advance you could use reserve to prevent the vector A from reallocating its contents. You'd nevertheless face the problem that references cannot be "reassigned". You can always use A.back() to refer to the last element.
You can use a function taking a reference argument which will be bound upon calling the function:
void do_stuff(std::vector<int> & recent)
{
// do stuff with recent
}
std::vector<std::vector<int>> A;
while (condition)
{
// add whatever to A
A.emplace_back(std::vector<int>{});
// do stuff with last element
do_stuff(A.back());
}
Another way to do it is with scope:
std::vector<std::vector<int>> A(1);
{
std::vector<int> &recent = A.back();
recent.emplace_back(50);
std::cout << A.back()[0] << std::endl; //prints 50
A.resize(2);
} // recent goes out of scope here
std::cout << A.size() << std::endl; //prints 2
{
std::vector<int> &recent = A.back(); // another recent indepedant of first one
std::cout << A[1].size() << std::endl; //prints 0
recent.emplace_back(20);
std::cout << A[1][0] << std::endl; // prints 20
}

Let's step through the code line by line.
vector<vector<int>> A(1);
vector<int> &recent = A.back();
Here we create a vector with one default-constructed vector<int> as its contents. We then bind a reference to the last and only element.
recent.emplace_back(50);
cout << A.back()[0] << endl; //prints 50
We now emplace 50 into the sole vector and print it.
A.resize(2);
Now we resize the vector. If space needs to be reallocated, all iterators, pointers and references to the contents are now invalid.
A[1] = vector<int>();
cout << A.size() << endl; //prints 2
This is fine, as there is enough space in A.
recent = A.back();
BANG
This assignment doesn't rebind recent, it tries to assign A.back() to the referencee. If space was reallocated for A, recent is no longer a valid reference, so we run off into the realm of undefined behaviour.
Quite honestly, using A.back() directly rather than maintaining a reference to it is probably your best bet. If you absolutely want to hold some kind of reference to the end, this is a reasonable use of a non-owning pointer.

From the discussion in the comments, it appears that your original problem was:
vector<vector<int>> very_long_name_that_cannot_be_changed;
and that you want a shorthand notation to access the last element of this:
auto& short_name = very_long_name_that_cannot_be_changed;
short_name.resize(100); // will expand the vector, but not change the reference
short_name.back().emplace_back(20); // presto, quick accesss to the last element.
This is proof against resizing, because the reference just tracks the vector, not its last element.

Related

How do vector elements preserve their original address after a vector std::move?

As you can see in the output, the objects of the vector pre not only "moved" to the vector post, but also preserved their original address space in memory. What is really going on behind this move? Is this behaviour expected? Say I need to have a separate vector of pointers to these objects, is it safe to assume that after this move the objects will always have their original addresses?
Actually, I have a class containing a vector like this and the vector of pointers I mentioned as members. I have also deleted the copy ctors, and defined the move ones for the class.
#include <iostream>
#include <vector>
struct B {
int val = 0;
B(int aInt) : val(aInt) { };
};
int main() {
std::vector<B> pre;
pre.push_back(B(1));
pre.push_back(B(2));
std::cout << "pre-move:\t" << (void*)&pre.at(0) << '\n';
std::cout << "pre-move:\t" << (void*)&pre.at(1) << '\n';
std::vector<B> post(std::move(pre));
std::cout << "post-move:\t" << (void*)&post.at(0) << '\n';
std::cout << "post-move:\t" << (void*)&post.at(1) << '\n';
return 0;
}
Output:
pre-move: 0x1d7b150
pre-move: 0x1d7b154 <------|
post-move: 0x1d7b150 |
post-move: 0x1d7b154 <------|
A vector is basically nothing more than a pointer to heap-allocated memory, the current length and the current capacity of the vector.
By "moving" a vector, all you're doing is copying those values, and resetting the values of the moved-from vector.
For the data of the vector, it's basically equivalent to
original_pointer = some_place_in_memory;
new_pointer = original_pointer; // Copies the *value* of original_pointer
original_pointer = nullptr;
There's no need to allocate new memory and copy the data in the vector.
The whole point of the move operation is to avoid copying the elements, so if they got copied(there is no such thing as truly "moving" the memory) the move would be just a copy.
Vectors are usually implemented as 3 pointers: begin,end and capacity. All point to a dynamically-allocated array. Then moving the vector is just copying those three pointers and so the array and elements just change their owner.
I think it should be safe to assume that pointers to the elements remain valid.
It will be clear, if we write semantically equal code without std::vector:
B* pre = new B[2]; // Declare std::vector<B> and allocate some space to make the following line correct
B[0] = 1; // pre.push_back(B(1));
B[1] = 2; // pre.push_back(B(2));
B* post = pre; // std::vector<B> post(std::move(pre));
Actually, vector move boils down to pointer copying without reallocation. Data which the pointer points at remains in it's place, so addresses of vector elements do not change.
In this code example after the fourth line, both pre and post point to the same data with same address.
std::vector is a wrapper for a pointer to array with some additional functionality. So after doing std::vector<B> post(std::move(pre));, post will contain a pointer with the same value which was in pre.

Printing a Pointer Vector's Value

Say I have a vector of pointers, but I want to print out the value that the first pointer points to. What syntax would I use? I'm learning to work with pointers and I can't find this specific case anywhere on google myself.
Ex:
vector<int*> vec;
//fill the vector however it needs to be
cout << vec[0];
//This prints an address but I want the value that address points to
You can get the value of what a pointer is pointing at by dereferencing the pointer with *, so you'd get *(vec[0]), this would require the vector to at least have one element in the first place and point to something valid of course.
That being said, please reconsider if you actually need to use pointers at all, a lot of the time I see pointers being used without a good reason. Use normal ints if you don't have a good reason to justify using pointers.
You just have to dereference the pointer you obtain with vec[0], i.e. you'd have to write
cout << *(vec[0]) << endl;
Note, however, that you did not reserve memory for the pointer; probably you even want to manage plain arrays of ints.
A working (even if one still may discuss how meaningful it is to have a vector maintaining pointers to ints) example could look as follows:
int main() {
int a[] = { 1,2,3 };
std::vector<int*> vec;
vec.push_back(a);
cout << *(vec[0]) << endl;
cout << vec[0][1] << endl;
}
Output:
1
2

Is it possible to initialize new position of vector using array without push_back (C++ STL)

While I was doing experiment with vector in C++, I was facing some strange problem. May be it was because of my little knowledge of C++ STL. I am using Code::Blocks 16.01 IDE having GNU GCC compiler in it.
When I run this code:
vector <int> vec;
vec.push_back(66);
vec.push_back(12);
cout << vec[1] << endl;
The output is obviously correct i.e. 12.
Again, when I run this code:
vector <int> vec;
vec.push_back(66);
vec.push_back(12);
vec[1] = 18;
cout << vec[1] << endl;
This time the output is also correct i.e. 18.
This time I did push_back() for only first 2 elements of vector. But initialized the value of 6th element using array and after running the following code:
vector <int> vec;
vec.push_back(66);
vec.push_back(12);
vec[5] = 18;
cout << vec[5] << endl;
The output is again fine i.e. 18.
But, when I run the code below, the console window crashes immediately. I don't know why.
vector <int> vec;
vec.push_back(66);
vec.push_back(12);
cout << vec[1] << endl;
vec[5] = 18;
cout << vec[5] << endl;
Once I used cout once, the program crashes. Why this is happening? Am I missing something about the connection of vector with array? I want to know the proper way to handle vector using array or is it bad practice to use array to handle vector?
Am I missing something about the connection of vector with array?
No you do not. If you misbehave the same way with array you will have the same issue:
int array[2];
array[5] = 18; // this is undefined behaviour
std::cout << array[5] << std::endl; // this is undefined behaviour as well
you can even see desired output in some environment, but it could crash on another, or start to crash when you change your code. That is problem with UB - it is unpredictable. And accessing elements out of range has similar consequences with array and std::vector, difference - you can resize std::vector but cannot do that to array.
When you access an element of a std::vector that is outside of the size of the vector, you get undefined behavior. Undefined behavior means what it sounds like - anything could happen. This is why sometimes your program will crash, and other times it works fine. You should not rely on undefined behavior for anything, instead you should resize your vector using std::vector::resize before inserting something outside of the size of the vector.

Pass array to function without so it would not change original array no matter what

I have a function that performs some magic on the array that I am passing. But the original array should be intact. Unfortunately it is changing its content based on what is happening in the array.
Can you help me, please?
Function:
void test(int* array) {
array[0] = 1; // EDIT: Added missing line
std::cout << "Inside: " << array[0] << endl;
}
int main() {
int *testArray = new int[1];
testArray[0] = 0;
std::cout<<testArray[0]<<endl;
test(testArray);
std::cout << "Outside: " << testArray[0] << endl;
}
Current result is:
0
Inside: 1
Outside: 1
Result I would want to have:
0
Inside: 1
Outside: 0
Is this possible?
It sounds like you want to pass array by value not by reference. You are passing pointer to a first element here. So, any changes which you perform to that array inside that function will be reflected to original array.
The other problem is you haven't posted fair amount of code regarding the problem you want to solve. I am assuming you want functionality like this.
See live demo here.
#include <iostream>
void test(const int* array) {
array[0]=1;
std::cout << "Inside: " << array[0] << std::endl;
}
int main() {
int *testArray = new int[1];
testArray[0] = 0;
std::cout<<testArray[0]<<std::endl;
test(testArray);
std::cout << "Outside: " << testArray[0] << std::endl;
delete[] testArray;
}
Compiler will give you following errors:
Error(s):
source_file.cpp:4:13: error: read-only variable is not assignable
array[0]=1;
~~~~~~~~^
1 error generated.
You should not use new[] to allocate dynamic arrays in C++. 99% of the time you should be using std::vector If you want dynamic array in C++.
Avoid using C compatibility features...
void test( std::array<int, 1> a )
{
a[0] = 1; // fine
std::cout << "Inside: " << a[0] << endl;
};
int main()
{
std::array<int, 1> testArray;
testArray[0] = 0;
std::cout<<testArray[0]<<endl;
test(testArray);
std::cout << "Outside: " << testArray[0] << endl;
}
If you need the size determined at runtime, use std::vector instead of std::array.
EDIT: As others have pointed out, it seems like you want to either pass the array by value instead of by reference, thus copying the elements of the array and modifying only the copy, or you want to avoid modifying any part of the array altogether. I'll elaborate on both parts a bit more:
In C++, there is near to no distinction between arrays and pointers. Note that both your variable testArray and your parameter array are pointers to the beginning of an array. If you use array to modify any part of the underlying array, what you actually do is modify the memory are that is described by both testArray and array. If you don't want to modify any part of the array at all, it would be helpful to use the const qualifier, as Destructor already wrote in his answer. If you however want to keep the original array but still want to make some modifications inside the function, the following still applies:
To keep the array from being modified, the only general way that works is to copy all of its elements by creating a new array of the same size, copying all elements from the input array to the copy and then working only on the copy, which should be deleted after the function has finished.
My personal answer:
I would recommend that you look into some of C++'s data structures, especially std::vector. If you pass it by value (not by reference), vector takes care of all the necessary copy operations I just described and in all cases, you can use it in the same way as an array, while it provides lots of additional features (i.e. dynamic size, deletion and insertion of elements, simplified iteration, ...).

In C++, when is destructor called automatically for local vector?

For example, say I have the following code:
vector<vector<int>> big;
for (int i=0;i<3;++i){
vector<int> small;
small.push_back(3*i+1);
small.push_back(3*i+2);
small.push_back(3*i+3);
big.push_back(small);
}
for (vector<int> s:big){
for (int a:s){cout<<a<<" ";}
cout<<endl;
}
The cout gives result that big contains the value for vector small in each for loop, I am confused because I thought the vector small will be destructed automatically in each for loop.
Why does the big still have access to the correct value?
Thanks!
When you execute:
big.push_back(small);
a copy of small is added to big. You can verify that they are two different objects by executing the following:
std::cout << (void*)&big.back() << std::endl; // big.back() returns a reference to the copy.
std::cout << (void*)&small << std::endl;
You can also verify that they hold the data of the vectors independently. You can print the pointers that hold the data.
std::cout << (void*)big.back().data() << std::endl;
std::cout << (void*)small.data() << std::endl;
That is because you used big.push_back(small); which made a copy of small vector and when small vector was destroyed at the end of the loop the copy was not effected
std::vector<T>::push_back() is copy-based. It creates a copy of the argument, which in this case is small, and stores it in big.
So you're NOT seeing the elements from small, but from big actually.