struct Word {
string wordName; //loses its value
vector<string> contents; //irrelevant for the question
int numContents = 0; //maintains its value
};
vector<Word*> wordMsgs;
int main()
{
vector<string> result;
result.push_back("wordName");
result.push_back("shouldEnterIf");
Word curr;
//New Word
if (result[1] != "") {
Word w;
w.numContents = 10; //just a testing #, suppose to represent size of vector
wordMsgs.push_back(&w);
w.wordName = result[0]; //name of Word
//here w.wordName and (*wordMsgs[0]).wordName display the same string; result[0]
curr = w;
}
//here curr.wordName displays result[0] but (*wordMsgs[0]).wordName doesn't. However, (*wordMsgs[0]).numContents returns 10 as expected
}
}
So I have a vector of struct references, assuming the push_back() method for vectors only pushes a copy to the end of the vector. I create an instance of my Word struct and put that reference into my vector, wordMsgs. I then edit the struct it is pointing too and then my string variable is lost upon leaving the if statement! However, my int variable never loses its value.
I don't understand because I used pointers (and I'm in C++ so I thought strings were a-okay) and I wouldn't think this is a local variable issue...
The variable w in:
if (...) {
Word w;
wordMsgs.push_back(&w);
...
}
Is allocated on the stack inside the scope of the if statement.
You are then adding its address to your vector of pointers.
Once outside the if statement, this variable is deallocated and the value at that address is no longer "solid", so you essentially have a "garbage address" in that vector.
From here onward, the behavior of your program is mostly unpredictable...
Here is a minimal example of what is going wrong in your code:
#include <string>
#include <vector>
#include <iostream>
int main() {
std::vector<string*> words; // why pointers?
if (true) { // scope starts here
std::string word = "muh";
words.push_back(&word); // dont do that !!
} // scope ends here
std::cout << *words.back();
}
By the time you access the string by dereferencing the pointer, the string is long gone, because word is destroyed when it goes outof scope. Dereferencing the pointer is undefined behaviour. You shouldnt have raw pointers in the vector when you dont need to. You probably wanted something like this:
#include <string>
#include <vector>
#include <iostream>
int main() {
std::vector<string> words;
if (true) {
std::string word = "muh";
words.push_back(word); // vector stores a copy ! and manages lifetime of its elements
}
std::cout << words.back();
}
You have a vector of pointers, not refrences. When you push back the adress of the locally defined struct Wors w by using &w you will lose that data directly when you go out of the if statement. You need to allocate and deallocate memory for the vector. Best to either just push objects in the vector or use a smart pointer.
Related
I have made a recursive way of printing all of the elements of a vector, but it returns nonsense! and it throws a really strange exception:
Exception thrown: read access violation.
std::vector<int,std::allocator<int> >::operator[](...) returned nullptr.
And it outputs: 12358000
This is the code. What is the mistake I have made?
#include <iostream>
#include <vector>
using namespace std;
int printVec(vector<int>* foo) {
if ((*foo).empty())
return 0;
else {
cout << (*foo)[0];
printVec(foo + 4);
}
}
int main() {
vector<int> ref{ 1,2,3,4,5,6,7,8,9,0 };
printVec(&ref);
}
foo is a pointer to a std::vector<int>.
foo + 4 is adding 4 lots of sizeof(std::vector<int>) to foo in pointer arithmetic. There is not a std::vector at that location, so the behaviour of printVec(foo + 4) is undefined.
The expression (*foo)[0] is calling the overloaded [] operator on a std::vector which access the first element in the vector. If there is no element at that position then the behaviour of the program is undefined.
What is the mistake I have made?
You are using a pointer to a single vector and treat it as if it points into an array of std::vector<int>. It is only allowed to increment pointers that point to elements in arrays (actually you are allowed to get a pointer one past an object, but not more). A single std::vector is not an array and your code invokes undefined behavior by incrementing foo here: printVec(foo + 4);.
If you want to "point to" elements of the vector use iterators:
#include <iostream>
#include <vector>
using namespace std;
template <typename IT>
void printVec(IT current, IT end) {
if (current == end) return;
else {
cout << *current;
printVec(current+1,end);
}
}
int main() {
vector<int> ref{ 1,2,3,4,5,6,7,8,9,0 };
printVec(ref.begin(),ref.end());
}
What is the structure of a std::vector?
You need not know nor care. If you want to iterate elements use iterators. If you want to access the underlying array use .data().
As the title describes, I am trying to pass the pointer to the data of a std::vector into a function expecting a double pointer. Take as an example the code below. I have an int pointer d which is passed to myfunc1 as &d (still not sure if call it the pointer's reference or what), where the function changes its reference to the beginning of an int array filled with 1,2,3,4. However, if I have a std::vector of ints and try to pass &(vec.data()) to myfunc1 the compiler throws the error lvalue required as unary ‘&’ operand. I have already tried something like (int *)&(vec.data()) as per this answer, but it does not work.
Just for reference, I know I can do something like myfunc2 where I directly pass the vector as reference and the job is done. But I want to know if it's possible to use myfunc1 with the std::vector's pointer.
Any help will be very much appreciated.
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
using std::vector;
void myfunc1(int** ptr)
{
int* values = new int[4];
// Fill all the with data
for(auto& i:{0,1,2,3})
{
values[i] = i+1;
}
*ptr = values;
}
void myfunc2(vector<int> &vec)
{
int* values = new int[4];
// Fill all the with data
for(auto& i:{0,1,2,3})
{
values[i] = i+1;
}
vec.assign(values,values+4);
delete values;
}
int main()
{
// Create int pointer
int* d;
// This works. Reference of d pointing to the array
myfunc1(&d);
// Print values
for(auto& i:{0,1,2,3})
{
cout << d[i] << " ";
}
cout << endl;
// Creates the vector
vector<int> vec;
// This works. Data pointer of std::vector pointing to the array
myfunc2(vec);
// Print values
for (const auto &element : vec) cout << element << " ";
cout << endl;
// This does not work
vector<int> vec2;
vec2.resize(4);
myfunc1(&(vec2.data()));
// Print values
for (const auto &element : vec2) cout << element << " ";
cout << endl;
return 0;
}
EDIT: What my actual code does is to read some binary files from disk, and load parts of the buffer into the vector. I was having troubles getting the modified vector out of a read function, and this is what I came up with that allowed me to solve it.
When you write:
myfunc1(&(vec2.data()));
You are getting the address of a rvalue. The pointed int* is so a temporary that is destroyed right after the call.
This is why you get this error.
But, as #molbdnilo said, in your myfunc1() function, you are reassigning the pointer (without caring to destroy previously allocated memory by the way).
But the std::vector already manages its data memory on its own. You cannot and you must not put your hands on it.
What my actual code does is to read some binary files from disk, and load parts of the buffer into the vector.
A solution could be to construct your std::vector by passing the iterator to the beginning and the iterator to the end of the desired part to extract in the constructor's parameters.
For example:
int * buffer = readAll("path/to/my/file"); // Let's assume the readAll() function exists for this example
// If you want to extract from element 5 to element 9 of the buffer
std::vector<int> vec(buffer+5, buffer+9);
If the std::vector already exists, you can use the assign() member function as you already did in myfunc2():
vec.assign(buffer+5, buffer+9);
Of course in both cases, you have to ensure that you are not trying to access an out of bounds element when accessing the buffer.
The problem is that you cannot take the address of data(), since it is only a temporary copy of the pointer, so writing to a pointer to it makes not that much sense. And that is good that way. You DO NOT want to pass data() to this function since it would overwrite the pointer with a new array and that would break the vector. You can remove one * from the function and only assign to it and not allocate the memory there. This will work, but make sure to allocate the memory in the caller (with resize, just reserve will result un undefined behavior, since data() is only a pointer to the beginning of the valid range [data(), data() + size()). The range [data(), data() + capacity ()) is not necessary valid.
I have the following struct:
#include <string>
#include <vector>
struct A {
std::string name;
int id;
};
And a vector containing A elements:
std::vector<A> a_vector;
I am trying to append an element to the vector and change its values using the following:
void test()
{
A a;
get_a(a);
//Up to this point I thought modifying this a object would mean modifying the back element of the vector. But it doesn't work as planned, doing this:
a.id = 2; //Doesn't modify the id of the element in the vector.
}
where get_a is defined as : (The code is simplified, in the real one I really need to pass a as argument and not get it as return)
void get_a(A& a) //This function normally assigns a in different ways
{
a_vector.emplace_back();
a = a_vector.back();
}
How can I do to have the a element be the same as the one in the vector? Do I really have to use pointers?
A a;
a = a_vector.back();
Here you're copy-assigning a_vector.back() to a. This is not a reference, so modifying a will not modify the element inside the vector.
You want this instead:
A& a = a_vector.back();
If you cannot immediately initialize your reference with a_vector.back(), consider using a pointer...
A* a;
// ...
a = &a_vector.back();
// ...
something(*a);
...or an index:
std::size_t a_idx;
// ...
a_idx = a_vector.size() - 1;
// ...
something(a_vector[a_idx]);
The pointer will work fine if you know that the vector won't get resized. If the vector resize, iterators and pointers will be invalidated.
The index will work fine even if the vector gets resized, as long as the elements are not removed/shifted around.
You need a reference to the object:
auto& a = a_vector.back();
Or, in a more compact manner:
a_vector.back().id = 2;
You're holding a copy, not the original object. That is why the object in vector does not get modified.
Answer to edited question: references can be assigned only during declaration. What you want is probably std::reference_wrapper, but anyway, please don't use it unless you have to.
(This question can be extended to others containers)
I can use a vector to storage a lot of data then it is important to know how the c++ manage the assignment to this container specially if I don't have a lot of memory.
a) When an assign is made to a function that return a vector the data on this vector is clean up immediately after the assign or it keep it until the last moment to receive the new data?
b) If the data is immediately destroyed, which state the vector during the function process to build the new data?
b.1) If the data has kept, that means I can have in memory two huge vectors (the old data and new data) until the assign be made (returned)?
c) And what about the move semantic? It try to use the space already created? (if the data is not destroyed immediately like asked in the first question)
To better demonstrate it I wrote a tiny program:
#include <vector>
#include <string>
using namespace std;
vector<string> func2() {
vector<string> f2(200000, string(20,'a'));
return f2;
}
vector<string> func4() {
vector<string> f4(400000, string(40,'b'));
//Now I have in memory 400000X40 + 200000X20?
return f4;
//after assignment I know I have just f4 content
}
vector<string> func1() {
vector<string> f1(100000, string(10,'c'));
return f10;
}
int main() {
vector<string> mVec;
mVec = func2(); //Create internally a vector with 20 units string with 20 'b' inside
mVec = func4(); //Create internally a vector with 40 units string with 40 'b' inside
mVec = func1(); //Create internally a vector with 10 units string with 10 'b' inside
return 0;
}
If the move semantic really use the existent space, how it works when mVec has less data and need to assign more (func2 to func4) or vice-versa (func4 to func1).
When func4() is called, right before return statement, you surely have two big vectors. And you have them in memory right up to the point of assignment.
As for c), vector is contigous container, so when you create new vector, it will be stored in memory as a whole, not one part in memory block 1 and the other part in memory block 127 (just an example).
My suggestion would be, to use passing vector (which will be refiled) by reference, and resize it correctly before filling, that way you may avoid asignment of whole vector.
Code sample (optimization suggestion):
#include <vector>
#include <algorithm> // std::fill
#include <string>
using namespace std;
void func2(vector<string>& vec) {
vec.resize(200000);
fill(vec.begin(), vec.end(), string(20, 'a'));
}
void func4(vector<string>& vec) {
vec.resize(400000);
fill(vec.begin(), vec.end(), string(40, 'b'));
}
void func1(vector<string>& vec) {
vec.resize(100000);
fill(vec.begin(), vec.end(), string(10, 'c'));
}
int main() {
vector<string> mVec;
func2(mVec); //Create internally a vector with 20 units string with 20 'b' inside
func4(mVec); //Create internally a vector with 40 units string with 40 'b' inside
func1(mVec); //Create internally a vector with 10 units string with 10 'b' inside
return 0;
}
The code below is just the isolated problem. I want to not only reserve the size of the vector but also initialize the contents so that if a vector element within the size range is suddenly assigned a value, it won't throw a vector subscript out of range error. I'm basically replacing a fixed-size array that is used throughout a large amount of code and I want the same functionality without having to add "item.push_back(newItem)" to throughout the file. I tried putting a for loop inside the Node constructor that just added but for some reason the .push_back() method was unrecognized.
How would I not only reserve the size, but also initialize the contents within the constructor?
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>
using std::cout;
using std::endl;
using std::string;
using std::vector;
int maxSize = 3;
struct Item{
string key;
string value;
};
struct Node{
int count;
vector<Item> items;
Node()
{
items.reserve(maxSize + 1);
}
};
int main(int argc, char *argv[])
{
Item item;
item.key = "Hi";
item.value = "there";
Node *p = new Node();
p->items[1] = item; // Error: vector subscript out of range
cout << p->items[1].key << " " << p->items[1].value << endl;
return 0;
}
Reserving only allocates underlying memory, for purposes of optimisation. If you don't know you need it, you don't. If you don't know you need it, and you wrote it, you shouldn't have done.
What you're trying to do is make the vector actually take on a size, and fill up with elements that you can immediately access via operator[].
To do so, write resize instead of reserve.
In fact, since you're doing so on a member in the constructor of its container, what you really should be doing is initialising the vector by calling its proper constructor:
struct Node
{
int count;
vector<Item> items;
Node()
: items(maxSize + 1)
{}
};
You're looking for resize instead of reserve