Manage memory on vector assignment - c++

(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;
}

Related

Why does my struct lose the string value even with references?

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.

Using vector::back() to modify vector element

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.

Can't explain why pointers become dangling when creating a vector of vectors of pointers

Consider the following code:
#include <iostream>
#include <vector>
using namespace std;
class SomeClass {
public:
SomeClass(int num) : val_(num) {}
int val_;
int val() const { return val_; }
};
// Given a vector of vector of numbers, this class will generate a vector of vector of pointers
// that point to SomeClass.
class Generator {
public:
vector<SomeClass> objects_;
vector<vector<SomeClass*> > Generate(const vector<vector<int> >& input) {
vector<vector<SomeClass*> > out;
for (const auto& vec : input) {
out.push_back({});
for (const int num : vec) {
SomeClass s(num);
objects_.push_back(s);
out.back().push_back(&objects_.back());
}
}
return out;
}
};
int main() {
Generator generator;
auto output = generator.Generate({{2, 3}, {4, 5}, {6}});
for (const auto& vec : output) {
for (const auto* obj : vec) {
printf("%d ",obj->val());
}
printf("\n");
}
return 0;
}
The Generate method in the Generator class will simply convert the vector of vector of ints to a vector of vector of pointers to SomeClass.
SomeClass is simply a container for a simple int value with a getter method.
I would expect the following output:
2 3
4 5
6
However, I get the following output:
junk_integer junk_integer
4 5
6
It seems the pointers in the first row become dangling pointers. What is wrong with this code?
You're storing pointers into a vector, then adding elements to the vector. Since you're not reserving enough space for all the elements you're adding, when the vector resizes it invalidates all the pointers to the old data.
You'll either have to reserve enough space before storing the pointers, store the pointers after you've stored everything in the vector you need to store, or not store pointers (maybe store an index, instead).
All operations that increase the number of elements in a std::vector, including push_back(), invalidates all iterators (including pointers) that refer to elements of the vector, if the resizing results in a change of vector capacity.
Your code is doing
objects_.push_back(s);
out.back().push_back(&objects_.back());
within a loop. Every call of objects_.push_back() invalidates iterators of objects_, and therefore can result in out.back() containing invalid (dangling) pointers.
You are storing pointers to objects contained in generator.objects_. Some of them become dangling pointers when you call push_back() on that.
In general, storing pointers to objects in a std::vector is a bad idea.

Insert values in a vector of int sets in C++

I want to insert values into a vector of set<int>, which is defined using typedef as given below:
typedef std::set<int> blockSet_t;
std::vector<blockSet_t>* myvector
I want to have values such that myvector[0] has a set of ints, myvector[1] has a different set of ints and so on.
Currently, I am passing this vector to a function which is parsing a file that has the set of integers.
Like:
main()
{
std::vector<blockSet_t> myvector;
filereader(myvector);
}
I read the set from the file and store it in a different blockSet_t myset;
I am using the following loop to store this set to a specific location in the vector:
filereader(&myvector)
{
for(int i=0;i<size;i++)
{
myvector.push_back(myset); // It does not give error but I don't know myset is stored in which location
//what I want is to have someting like this
myvector[i].push_back(myset); //so I can store different sets at different locations
}
}
I also could not figure out, how to display the values from within thevector.
Since it is a vector of sets, I want to display each set (on a different vector index).
Any help in this regard is much appreciated.
Thanks.
First of all, push_back function is called push_back because it pushes an object back.
It means that, if your vector is empty and you call push_back your pushed object will have 0 location.
If your vector has n objects inside it, this means after push_back your pushed object will have n-th index.
myvector.push_back(myset);
std::cout<<"index of myset is "<<myvector.size()-1<<std::endl;
Secondly, if you want to print values, you have to create your own operator<< overloading function for std::ostream class. This is a common way of printing values in C++.
Assume you want to print set in curly {} brakets and vector in square [] ones:
#include <ostream>
inline std::ostream& operator<<(std::ostream &os,const blockSet_t &mySet)
{
os<<"{ ";
for(const auto &value:mySet)
{
os<<value<<' ';
}
os<<"};
return os;
}
inline std::ostream& operator<<(std::ostream &os,const std::vector<blockSet_t> &myvector)
{
os<<"[ ";
for(const auto &mySet:myvector)
{
os<<mySet<<' ';
}
os<<"];
return os;
}
Next, you have to cout your object like this:
#include <ostream>
main()
{
std::vector<blockSet_t> myvector;
filereader(myvector);
std::cout<<myvector<<std::endl;
}

cannot access the vector members of a class

I have tried to access the members of a class Part that are vector elements of type integer inside the vector tasks.
#include <iostream>
#include <vector>
using namespace std;
class Part{
vector<int> tasks;
public:
void setTasks(void);
void getTasks(void);
};
void Part::setTasks(void){
vector<int>::iterator it;
int i=1;
for (it = this->tasks.begin(); it != this->tasks.end(); ++it)
{
*it=i;
i=i+1;
}
}
void Part::getTasks(void){
vector<int>::iterator it;
for (it = this->tasks.begin(); it != this->tasks.end(); ++it)
cout<<*it<<"\t";
}
int main()
{
Part one;
one.setTasks();
one.getTasks();
return 0;
}
I am simply trying to access the values and print them yet failing. There is no compilation error. In run-time, nothing is outputted in the terminal. Where is the error?
A default constructed vector has zero size, so the for loop in setTasks is never entered (since the begin() and end() iterators are the same at that point). If you set an initial size to the vector your code will work as intended. For instance, try adding the following at the beginning of setTasks
tasks.resize(10); // sets vector size to 10 elements, each initialized to 0
Another way to write that function would be
#include <numeric>
...
void Part::setTasks(void){
tasks.resize(10);
std::iota(tasks.begin(), tasks.end(), 1); // requires C++11
}
You could also set the initial size of the vector in the default constructor of Part if you wanted to. In that case add the following public constructor
Part() : tasks(10)
{}
Yet another way to achieve setting the size upon construction would be
class Part{
vector<int> tasks = vector<int>(10); // requires C++11
The size of your vector is 0 when you call setTasks(). Your iterator doesn't get you into the for loop at all. You need to think about what exactly you want your setTasks() to do. How many elements of the vector did you intend to set? You should either define your vector with that size, or use that many number of push_backs instead to set your vector to the desired value.
Your vector is empty. Try giving it a size. For example, vector<int> tasks(10). See option 3 in this.
Alternatively, you can use a "back-insert" iterator (#include <iterator>), which internally calls std::vector::push_back, like this:
void Part::setTasks(void){
auto back_it = std::back_inserter(tasks);
for(int i = 0; i < 10; ++i)
*back_it++ = i;
}
This kind of iterator is especially useful in algorithms where your destination size is unknown. Although if you know the size in advance, you should use reserve/resize or specify the size at construction, since push-ing back into a vector can sometimes be slow due to re-allocation.