Order of inserting elements into a 2D Vector in c++ - c++

I was practicing with C++ vectors, and found a problem when I was inserting elements into a 2D vector. In the following example:
#include <iostream>
#include <vector>
void fillVector(std::vector<std::vector<int> > &vec) {
std::vector<int> list1;
vec.push_back(list1);
list1.push_back(1);
list1.push_back(2);
std::vector<int> list2;
list2.push_back(3);
list2.push_back(4);
vec.push_back(list2);
return;
}
int main() {
std::vector<std::vector<int> > vect;
fillVector(vect);
std::cout << "vect size: " << vect.size() << std::endl;
for(int i = 0; i < vect.size(); i++) {
std::cout << "vect in size: " << vect.at(i).size() << std::endl;
}
}
the size of the first inner-list is 0, and the size of the second inner-list is 2. The only difference between list1 and list2 is that list1 is first inserted into the vec 2D vector, before elements are inserted into it, while elements are first inserted into list2, before it is itself inserted into the 2D vector. After returning from the function, the elements inserted into list1 are not printed, and its size remains the same.
I also attempted the first method with pointers instead,
std::vector<int> *list3 = new std::vector<int>();
vec.push_back(*list3);
list3->push_back(5);
list3->push_back(6);
However, the size of list3 when read from the calling function is still 0.
I don't understand the difference between the two approaches. Why does the list have to appended after it's elements are inserted?

It almost seems like you are expecting python-like behavior? In any case, in C++ the distinction between references, pointers, and values is very important.
Your fillVector function has the right idea, as it takes a reference to a 2D vector std::vector<std::vector<int> > &vec - notice the &. However, when you create list1, and use push_back() right away
std::vector<int> list1;
vec.push_back(list1);
you are pushing the empty vector. push_back() will create a copy of this vector, which will be contained in vect (in main), and is a completely separate vector from list1.
At this point, if you want to access the vector already pushed, you can use back(), which returns a reference to the last element in the vector vec, that is, the last one pushed.
vec.back().push_back(1);
vec.back().push_back(2);
list2 you modify before pushing back, so when the copy is made, it is made of the already modified vector. Your attempt with list3 doens't really change things much, you dereference the pointer when you push_back() and a copy is made all the same. You could make vect be std::vector<std::vector<int>*>, but I'd strongly advice against it, as you have to do manual memory management - using new.
Note: While it's important for you to learn at some point, you should really try to avoid using pointers whenever possible, specially RAW pointers (look at smart pointers instead). std::vector, and all other std containers I know of, do their own memory management - they are sure to do it more efficiently than you, and BUG FREE.
I would suggest that you simply work on the last vector pushed, as such:
void fillVector(std::vector<std::vector<int> > &vec) {
vec.push_back(std::vector<int>());
vec.back().push_back(1);
vec.back().push_back(2);
vec.push_back(std::vector<int>());
vec.back().push_back(3);
vec.back().push_back(4);
return;
}
as you can see it's pretty much the same code repeated twice, so you can easily loop to get this or other results.

vector.push_back(var) makes a copy of var and inserts it into the vector. If you use push_back() on an empty list, it copies the empty list into the vector. Changing values in the list after this does not affect the copy that was inserted into the vector. This is because you are passing an actual object to push_back(), not a pointer to an object.
In the third example, you take a step in the right direction, but you de-reference the list before you pass it in, so push_back() makes a copy of what is at that address.
A simple solution to the problem is to always set your values before you insert the list into the vector.
If you wish to be able to change the values after the list is inserted, use vect.at(i).push_back(val) to add a value to the list at i.
You could also make the vector contain pointers to other vectors, rather than the vectors themselves:
void fillVector(std::vector<std::vector<int> *> &vec) {
std::vector<int> *list1 = new std::vector<int>(); //Remember to allocate memory since we're using pointers now
list1->push_back(1);
list1->push_back(2);
vec.push_back(list1); // Copy the pointer that is list1 into vec
std::vector<int> *list2 = new std::vector<int>();
vec.push_back(list2); // Copy the pointer that is list2 into vec
list2->push_back(3);
list2->push_back(4);
return;
}
int main() {
std::vector<std::vector<int> *> vect; // Vector of pointers to vectors
fillVector(vect);
std::cout << "vect size: " << vect.size() << std::endl;
for(int i = 0; i < vect.size(); i++) {
std::cout << "vect in size: " << vect.at(i)->size() << std::endl;
}
}
std::vector<std::vector<int> *> vec = new; // Vector of pointers

When you put something into a std::vector, the vector stores a copy. Manipulating the original will have no effect on the copy. If you put a pointer into a vector of pointers the vector still stores a copy of the pointer. Both the original and the copy in the vector point at the same memory, so you can manipulate the referenced data and see a change in the referenced data.
So...
std::vector<int> list1;
vec.push_back(list1);
list1.push_back(1);
list1.push_back(2);
puts a copy of the emptylist1 into vec. Then copies of 1 and 2 are placed into the original list1. The copy of list1 in vec is unaffected.
Writing this as
std::vector<int> list1;
vec.push_back(list1);
vec.back().push_back(1);
vec.back().push_back(2);
will correct this. As will a slightly cleaner version
vec.push_back(std::vector<int>());
vec.back().push_back(1);
vec.back().push_back(2);
as it doesn't have a waste list1 hanging around cluttering up the scope.
And
vec.push_back(std::vector<int>{1,2});
will simplify even further if your compiler supports C++11 or better.
On the other hand...
std::vector<int> list2;
list2.push_back(3);
list2.push_back(4);
vec.push_back(list2);
puts copies of 3 and 4 into list2 and then puts a copy of list2, complete with copies of the copies of 3 and 4.
Similar to above,
std::vector<int> list2{3,4};
vec.push_back(list2);
can reduce the workload.
Unfortunately your experiment with list3 fails because while list3 is a pointer vec does not hold a pointer, so list3 is dereferenced and the vector referenced is copied. No pointer to the data list3 references is stored, and vec contains an empty vector for the same reason as above.
std::vector<int> *list3 = new std::vector<int>();
vec.push_back(*list3);
list3->push_back(5);
list3->push_back(6);
A few notes on storing pointers in vectors
The vector only stores a copy of the pointer. The data pointed at must be scoped in such a way that it will not be destroyed before the vector is done with it. One solution is to dynamically allocate the storage.
(This applies to dynamically allocated pointers in general) If you dynamically allocate, sooner or later someone has to clean up the mess and delete those pointers. Look into storing smart pointers rather than raw pointers and not storing pointers at all.
Familiarize yourself with the Rule of Three. vector looks after itself, but if you have two copies of the vector and you remove and delete a pointer from only one of them, you're going to have some debugging to do.

Related

C++ : Size of the array didn't change even after deleting it

Assuming I have this program below
The size of the array is 3 and basically when deleting it and then try to print the size again , I still get the value 3 .
Why is that ?
#include <vector>
#include <iostream>
int main(){
std::vector<int*> v;
v.push_back(new int{1});
v.push_back(new int{2});
v.push_back(new int{3});
std::cout << v.size() << '\n';
for (auto iptr : v){
delete iptr;
}
std::cout << v.size() << '\n';
return 0;
}
Because you deleted whatever was pointed by the pointer
vector<CPerson *> Persons;
As far as the vector is concerned, it still has 3 pointers pointing somewhere.Beware, you are not permitted to dereference them pointers, otherwise you will invoke undefined behavior).
You have a vector of pointers. Using delete on a pointer in that vector will delete the thing the pointer is pointing at. It does not remove the pointer itself from the vector, and so does not change the size of the vector. Why should it? How could it?
Here the code you were looking for (I guess):
for (int i = 0; i < Persons.size(); i++)
{
CPerson *Student = Persons[i];
if (Student)
{
Persons.erase(Persons.begin() + i);
delete Student;
i--;
}
}
Note:
vector::erase is (one way) to remove items from a vector.
The cast is unnecessary, because your base class has a virtual destructor.
Erasing items from a vector while you are iterating through it is tricky because the size of the vector changes when you erase an item and all the later items in the vector move down one place, that why I have i-- to compensate, without that you will at least skip items and at worst iterate off the end of the vector.
When you delete the pointers that are in the vector, it just deletes the memory that you assigned to them with new, it doesn't do anything about the fact that you have a pointer, there for you are cleaning up memory that is outside of the vector, the vector will clean it self up at the end of the scope. (In this case, the end of the function.) Just remember, the vector doesn't clean up your new memory, you have to clean that up.

C++: how to loop through integer elements in a vector

I would like to loop through elements of a vector in C++.
I am very new at this so I don't understand the details very well.
For example:
for (elements in vector) {
if () {
check something
else {
//else add another element to the vector
vectorname.push_back(n)
}
}
Its the for (vector elements) that I am having trouble with.
You'd normally use what's called a range-based for loop for this:
for (auto element : your_vector)
if (condition(element))
// whatever
else
your_vector.push_back(something);
But note: modifying a vector in the middle of iteration is generally a poor idea. And if your basic notion is to add the element if it's not already present, you may want to look up std::set, std::map, std::unordered_set or std::unordered_map instead.
In order to do this properly (and safely), you need to understand how std::vector works.
vector capatity
You may know that a vector works much like an array with "infinite" size. Meaning, it can hold as many elements as you want, as long as you have enough memory to hold them. But how does it do that?
A vector has an internal buffer (think of it like an array allocated with new) that may be the same size as the elements you're storing, but generally it's larger. It uses the extra space in the buffer to insert any new elements that you want to insert when you use push_back().
The amount of elements the vector has is known as its size, and the amount of elements it can hold is known as its capacity. You can query those via the size() and capacity() member functions.
However, this extra space must end at some point. That's when the magic happens: When the vector notices it doesn't have enough memory to hold more elements, it allocates a new buffer, larger1 than the previous one, and copies all elements to it. The important thing to notice here is that the new buffer will have a different address. As we continue with this explanation, keep this in mind.
iterators
Now, we need to talk about iterators. I don't know how much of C++ you have studied yet, but think of an old plain array:
int my_array[5] = {1,2,3,4,5};
you can take the address of the first element by doing:
int* begin = my_array;
and you can take the address of the end of the array (more specifically, one past the last element) by doing:
int* end = begin + sizeof(my_array)/sizeof(int);
if you have these addresses, one way to iterate the array and print all elements would be:
for (int* it = begin; it < end; ++it) {
std::cout << *it;
}
An iterator works much like a pointer. If you increment it (like we do with the pointer using ++it above), it will point to the next element. If you dereference it (again, like we do with the pointer using *it above), it will return the element it is pointing to.
std::vector provides us with two member functions, begin() and end(), that return iterators analogous to our begin and end pointers above. This is what you need to keep in mind from this section: Internally, these iterators have pointers that point to the elements in the vector's internal buffer.
a simpler way to iterate
Theoretically, you can use std::vector::begin() and std::vector::end to iterate a vector like this:
std::vector<int> v{1,2,3,4,5};
for (std::vector<int>::iterator it = v.begin; it != v.end(); ++it) {
std::cout << *it;
}
Note that, apart from the ugly type of it, this is exactly the same as our pointer example. C++ introduced the keyword auto, that lets us get rid of these ugly types, when we don't really need to know them:
std::vector<int> v{1,2,3,4,5};
for (auto it = v.begin; it != v.end(); ++it) {
std::cout << *it;
}
This works exactly the same (in fact, it has the exact same type), but now we don't need to type (or read) that uglyness.
But, there's an even better way. C++ has also introduced range-based for:
std::vector<int> v{1,2,3,4,5};
for (auto it : v) {
std::cout << it;
}
the range-based for construct does several things for you:
It calls v.begin() and v.end()2 to get the upper and lower bounds of the range we're going to iterate;
Keeps an internal iterator (let's call it i), and calls ++i on every step of the loop;
Dereferences the iterator (by calling *i) and stores it in the it variable for us. This means we do not need to dereference it ourselves (note how the std::cout << it line looks different from the other examples)
putting it all together
Let's do a small exercise. We're going to iterate a vector of numbers, and, for each odd number, we are going to insert a new elements equal to 2*n.
This is the naive way that we could probably think at first:
std::vector<int> v{1,2,3,4,5};
for (int i : v) {
if (i%2==1) {
v.push_back(i*2);
}
}
Of course, this is wrong! Vector v will start with a capacity of 5. This means that, when we try using push_back for the first time, it will allocate a new buffer.
If the buffer was reallocated, its address has changed. Then, what happens to the internal pointer that the range-based for is using to iterate the vector? It no longer points to the buffer!
This it what we call a reference invalidation. Look at the reference for std::vector::push_back. At the very beginning, it says:
If the new size() is greater than capacity() then all iterators and references (including the past-the-end iterator) are invalidated. Otherwise only the past-the-end iterator is invalidated.
Once the range-based for tries to increment and dereference the now invalid pointer, bad things will happen.
There are several ways to avoid this. For instance, in this particular algorithm, I know that we can never insert more than n new elements. This means that the size of the vector can never go past 2n after the loop has ended. With this knowledge in hand, I can increase the vector's capacity beforehand:
std::vector<int> v{1,2,3,4,5};
v.reserve(v.size()*2); // Increases the capacity of the vector to at least size*2.
// The code bellow now works properly!
for (int i : v) {
if (i%2==1) {
v.push_back(i*2);
}
}
If for some reason I don't know this information for a particular algorithm, I can use a separate vector to store the new elements, and then add them to our vector at the end:
std::vector<int> v{1,2,3,4,5};
std::vector<int> doubles;
for (int i : v) {
if (i%2==1) {
doubles.push_back(i*2);
}
}
// Reserving space is not necessary because the vector will allocate
// memory if it needs to anyway, but this does makes things faster
v.reserve(v.size() + doubles.size());
// There's a standard algorithm (std::copy), that, when used in conjunction with
// std::back_inserter, does this for us, but I find that the code bellow is more
// readable.
for (int i : doubles) {
v.push_back(i);
}
Finally, there's the old plain for, using an int to iterate. The iterator cannot be invalidated because it holds an index, instead of a pointer to the internal buffer:
std::vector<int> v{1,2,3,4,5};
for (int i = 0; i < v.size(); ++i) {
if (v[i]%2==1) {
doubles.push_back(v[i]*2);
}
}
Hopefully by now, you understand the advantages and drawbacks of each method. Happy studies!
1 How much larger depends on the implementation. Generally, implementations choose to allocate a new buffer of twice the size of the current buffer.
2 This is a small lie. The whole story is a bit more complicated: It actually tries to call begin(v) and end(v). Because vector is in the std namespace, it ends up calling std::begin and std::end, which, in turn, call v.begin() and v.end(). All of this machinery is there to ensure that the range-based for works not only with standard containers, but also with anything with a proper implementation for begin and end. That includes, for instance, regular plain arrays.
Here is the quick code snippet using iterators to iterate the vector-
#include<iostream>
#include<iterator> // for iterators to include
#include<vector> // for vectors to include
using namespace std;
int main()
{
vector<int> ar = { 1, 2, 3, 4, 5 };
// Declaring iterator to a vector
vector<int>::iterator ptr;
// Displaying vector elements using begin() and end()
cout << "The vector elements are : ";
for (ptr = ar.begin(); ptr < ar.end(); ptr++)
cout << *ptr << " ";
return 0;
}
Article to read more - Iterate through a C++ Vector using a 'for' loop
.
Hope it will help.
Try this,
#include<iostream>
#include<vector>
int main()
{
std::vector<int> vec(5);
for(int i=0;i<10;i++)
{
if(i<vec.size())
vec[i]=i;
else
vec.push_back(i);
}
for(int i=0;i<vec.size();i++)
std::cout<<vec[i];
return 0;
}
Output:
0123456789
Process returned 0 (0x0) execution time : 0.328 s
Press any key to continue.

How to declare an array with a variable number of elements [C++]?

How do I [dynamically?] declare an array with an 'i' number of elements?
By using a container of the standard-library instead, most commonly std::vector<>.
Other solutions contain manual allocation and deallocation of memory and are probably not worth the effort.
You could use std::vector<T>. It works like
std::vector<int> a;
a.push_back(2); // add 2 to the array
a.push_back(4);
You could go on and on, and you won't need to worry about memory allocation issues.
You have to use pointers.
Example:
float *ptrarray = new float [10];
Basically, type * pointername = new type [i];
And don't forget to clean your memory:
delete [] ptrarray;
Or it will be reserved until the program ends.
Create a Dynamically Sized Array
std::size_t N = 10;
SomeType *a = new SomeType[N];
for (auto i=0; i<N; ++i) {
std::cout << a[i] << std::endl;
}
delete [] a;
A C++ array is a pointer to a contiguous chunk of memory containing a specific type. This snippet allocates space for an array of 10 SomeType instances, constructs the array which initializes each object using the default constructor, iterates over the array, prints out each element, and then deallocates the memory by calling delete [].
Key Points:
you are required to call delete [] to deallocate the array. The array form is required to ensure that the destructor is called on each object.
after allocation, there is no way to recover the size of the array.
you can iterate over the array by index (or pointer)
This is not the way to do what you want, keep reading.
Using a vector
std::size_t N = 10;
std::vector<SomeType> v(10);
for (auto iter=v.begin(); iter!=v.end(); ++iter) {
std::cout << *iter << std::endl;
}
This snippet uses std::vector which manages a contiguous block of memory for you. Note the usage of an iterator to walk over the vector instead of using indexing. Vectors do support direct indexing so the for loop used in the previous example would work here as well.
for (auto i=0; i<v.size(); ++i) {
std::cout << v[i] << std::endl;
}
Using an iterator is the best practice and idiomatic though using a for loop may not be but I digress.
Key Points:
memory management is automatic in the case of a vector
you can append using push_back
a vector knows how many elements are in it -- call v.size() to get the number of elements
iteration is performed using the iterators returned from v.begin() and v.end()
Just use std::vector. If you need raw access to the underlying pointer, then use &v[0] or v.data() but don't do that unless you need to. Also, don't use std::auto_ptr for arrays. You will be tempted to but don't do it.

C++ reorder std::vector elements using std::list of pointers

I ran into this problem when I tried to write out an new algorithm to reorder elements in std::vector. The basic idea is that I have std::list of pointters pointing into std::vector in such way that *list.begin() == vector[0], *(++list.begin()) == vector[1] and so on.
However, any modifications on list's element positions breaks the mapping. (Including appended pointers) When the mapping is broken the list's elements can be in random order but they point still into correct elements on vector. The task would be to reorder the elements in vector to correct the mapping.
Simplest method to do it (How I have done it now):
create new empty std::vector and resize it to equal size of the old vector.
iterate through the list and read elements from the old vector and write them into new vector. Set the pointer to point into new vector's element.
swap vectors and release the old vector.
Sadly the method is only useful when I need more capacity on the vector. It's inefficient when the current vector holding the elements has enough capacity to store all incoming elements. Appended pointers on the list will point into diffrent vector's storgate. The simple method works for this because it only reads from the pointers.
So I would want to reorder the vector "in place" using constant amount of memory. Any pointer that was not pointing into current vector's storgate are moved to point into current vector's storgate. Elements are simple structures. (PODs)
I'll try post an example code when I have time..
What should I do to achieve this? I have the basic idea done, but I'm not sure if it is even possible to do the reordering with constant amount of memory.
PS: I'm sorry for the (possibly) bad grammar and typos in the post. I hope it's still readable. :)
First off, why do you have a list of pointers? You might as well keep indices into the vector, which you can compute as std::distance(&v[0], *list_iter). So, let's build a vector of indices first, but you can easily adapt that to use your list directly:
std::vector<T> v; // your data
std::list<T*> perm_list; // your given permutation list
std::vector<size_t> perms;
perms.reserve(v.size());
for (std::list<T*>::const_iterator it = perm_list.begin(), end = perm_list.end(); it != end; ++it)
{
perms.push_back(std::distance(&v[0], *it));
}
(There's probably a way to use std::transform and std::bind, or lambdas, to do this in one line.)
Now to do the work. We simply use the cycle-decomposition of the permutation, and we modify the perms vector as we go along:
std::set<size_t> done;
for (size_t i = 0; i < perms.size(); while(done.count(++i)) {})
{
T tmp1 = v[i];
for (size_t j = perms[i]; j != i; j = perms[j])
{
T tmp2 = v[j];
v[j] = tmp1;
tmp1 = tmp2;
done.insert(j);
}
v[i] = tmp1;
}
I'm using the auxiliary set done to track which indices have already been permuted. In C++0x you would add std::move everywhere to make this work with movable containers.

How to have a vector of byvalue and use a vector of pointers in conjunction?

I have some vectors of class A objects:
std::vector<A> *V1;
std::vector<A> *V2;
etc
there is a function with a vector of pointers of A:
std::vector<A *> *arranged;
what I need to do is put the vectors from V1, V2 etc inside arranged without destroying them in the end, so I thought that a vector of pointers to those objects... is this possible?
if yes, can you give me an example of a iteration with variable V1 and add pointers of those objects into arranged?
imagine that you, temporarly, need to sort 3 vectors of objects into one vector but you don't want to mess up the memory of the 3 vectors.
ty,
Joe
You could write your own comparator. In this case, the comparator would work on A*. A simple example using int type:
void fun(vector<int*>* vec)
{
/////////
}
bool comp(int* lhs, int* rhs)
{
return *lhs < *rhs;
}
int main()
{
vector<int> first, second;
vector<int*> vec;
for(vector<int>::size_type i = 0; i < first.size(); ++i)
vec.push_back(&first[i]);
for(vector<int>::size_type i = 0; i < second.size(); ++i)
vec.push_back(&second[i]);
// write your own comparator! provided above: comp
sort(vec.begin(), vec.end(), comp);
fun(&vec);
return 0;
}
If I understand you correctly - you've several vectors containing some object type (A), and you want to create a new vector containing the composition of all members of the other vectors w/o actually copying the objects around, or otherwise disturbing their owning-vectors in any way?
First: does the new composite vector's life definitely exist such that none of its source-vectors will change? That is: you cannot just have raw pointers into the source-vectors from your composite vector if those pointers will be invalidated within the lifetime of your composite.
If the answer to that is anything other than "definitely, pointers will remain valid" then you need to consider using shared pointers, or something similar, such that altering the source-vectors will not leave your composite vector in an invalid state (i.e. not leave it pointing to random memory).
Assuming that the source-vectors will remain unchanged in terms of their own contents for the life span of your composite, then the simple answer is "Yes"
vector<A> source1;
vector<A> source2;
vector<A> source3;
vector<const A*> composite; // this is a sorted vector of the above vectors' contents (by pointer)
For the composite vector, you would need to put the contents (by copy) of source1-3 into it, and then sort it (or you could use a sorted container, and sort as you insert the elements). You'll need to define your own sorting operator, one that dereferences the pointers and applies whatever sort-algorithm on the target objects themselves.
Does that help you?