Iterate through vector of objects c++ - c++

I've been struggling with this problem so I hope someone can help me.
I am creating a vector of objects and I would like to access elements of this vector but when I push back en element into the vector and try to access it, it doesn't work.
Thank you for your answers.
class reserve{
private:
vector<float> coordinates;
public:
reserve(const reserve &);
void addACoordinate(float);
void readCoordinates();
int getDim();
};
reserve::reserve(const reserve &w){
int i;
for(i=0; i<coordinates.size(); i++){
coordinates[i] = w.coordinates[i];
}
}
void reserve::addACoordinate(float c){
coordinates.push_back(c);
}
void reserve::readCoordinates(){
int i;
cout << "the coordinates are : ";
for(i=0; i<coordinates.size(); i++){
cout << coordinates[i] << " ";
}
cout << endl;
}
int reserve::getDim(){
return coordinates.size();
}
Then I create a reserve with 2 coordinates and push it into the reserves vector.
vector<reserve> reserves;
reserve r;
r.addACoordinate(1.9);
r.addACoordinate(1.9);
r.getDim();
reserves.push_back(r);
cout << "size of reserves " << reserves.size() << endl;
for (vector<reserve>::iterator k = reserves.begin(); k != reserves.end(); ++k) {
k->getDim();
}
But the output of the iteration is 0, instead of 2. I don't understand why I am not accessing the reserve r.

The shown copy constructor is completely broken:
reserve::reserve(const reserve &w){
int i;
for(i=0; i<coordinates.size(); i++){
The newly-constructed coordinates class member is always completely empty when the copy-constructor runs. It obviously won't magically have any content right from the start, so coordinates.size() will always be zero here. The copy constructor will never actually copy the element that's being used to copy from, and the alleged "copy" of it will always have an empty vector. And changing this to w.coordinates.size() will result in memory corruption, unless you also replace the assignment in the loop with a push_back(). Speaking of push_back()...
reserves.push_back(r);
push_back() attempts to copy r into the vector, here. That's it's job. Guess what's going to be used to do that? Why, the broken copy constructor, of course. Even if r has an initialized coordinates vector, the broken copy constructor will ensure that reserves will end up with a class instance with a completely empty coordinates vector.
You can completely get rid of the copy constructor, here. It serves no useful purpose, and the default copy constructor will do everything correctly.
If you need to manually implement a copy constructor for your homework assignment, fix it so it properly push_back()s the values from the class instance being copied from, or manually copies the class members itself, whatever you need to do the job.

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.

Does a vector re-create all object on a push_back()?

I noticed that whenever I push_back() an object into a vector, all existing objects in the vector seem to be "re-created".
To test this I made the following test program down below. What the program does is to push_back() a few instances of MyClass into a vector and print the adresses of the current objects in the vector after each push_back(). The constructor and destructor prints a message with the adress of the object whenever an instance is created/destroyed.
#include "stdafx.h"
#include <vector>
#include <iostream>
using namespace std;
class MyClass {
public:
MyClass() {
cout << "Constructor: " << this << endl;
}
~MyClass() {
cout << "Destructor: " << this << endl;
}
};
void printAdresses(const vector<MyClass> & v) {
for (int i = 0; i < v.size(); i++) {
cout << &v[i] << endl;
}
}
int main()
{
vector<MyClass> v;
for (int i = 0; i < 4; i++) {
v.push_back(MyClass());
cout << "Contains objects: " << endl;
printAdresses(v);
cout << endl;
}
system("pause");
return 0;
}
Down below is the output I got from running the program. Every block is the output from one iteration of the loop.
Constructor: 00EFF6AF
Destructor: 00EFF6AF
Contains objects:
034425A8
Constructor: 00EFF6AF
Destructor: 034425A8
Destructor: 00EFF6AF
Contains objects:
034426F8
034426F9
Constructor: 00EFF6AF
Destructor: 034426F8
Destructor: 034426F9
Destructor: 00EFF6AF
Contains objects:
034424E8
034424E9
034424EA
Constructor: 00EFF6AF
Destructor: 034424E8
Destructor: 034424E9
Destructor: 034424EA
Destructor: 00EFF6AF
Contains objects:
034423F8
034423F9
034423FA
034423FB
Appart from the constructor and destructor of the item that was given as argument for the push_back(), destructors of other objects are called during a push_back() (but no corresponding constructors?). When taking a closer look at the adresses we see that the destructors belong to elements in the vector prior to the push_back(). Afterwards the vector contains other objects with different adresses, but the same number of elements (plus the newly push_backed).
What all this tells me is that all objects in the vector are destroyed and re-created after each push_back. If this is the case, why is that? It seems really inefficient when a vector gets too large. If it's not the case, then what exactly happens?
Vector pre-allocates some space.
For example, it can have an initial size of 2 elements. When you try to push the 3rd one, it re-allocates for 4 elements. When you try to push the 5th element, it re-allocates for 8 elements and so on.
That's why you have a .size() that reflects how many elements you have and a .capacity() that reflects what is the capacity (how many elements can be stored before a reallocation is needed).
You can manually handle that capacity by calling .reserve()
To answer your original question, reallocation requires copying all the elements from the previous array to a bigger one.
This requires to destroy the previous elements and to create some new ones.
If you saw the destructors calls but not the matching constructor calls, this is because it does not use the default constructor, but the copy constructor:
MyClass(const MyClass&)
This constructor takes one object has a parameter and uses it to construct and initialize a new object.
Moreover, as mentioned in the comments: even though the re-allocation/copy process can be seen as expensive, on average it is not and actually constitutes a constant time operation. This is because the cost of re-allocation and copy is amortized and future insertion because we do not re-allocate the exact space needed, but more space than necessary.

Instantiating an Array of Objects, but it is working like static objects

So i initialize an array of polynomials.
Polynomial* pArray;
pArray = new Polynomial[size];
for (int r = 0; r < size; r++){
pArray[r] = Polynomial(p.size);
}
When i change the coefficients of any polynomial in pArray, it changes all of them.
pArray[1].coefficients[0] = 12;
cout << pArray[1].coefficients[0] << "\n";
//print coefficients
for (int i = 0; i < size; i++){
for (int j = 0; j < p.size; j++){
cout << pArray[i].coefficients[j] << " ";
}
Output:
12 0 12 0 12 0
I don't want this to happen. When I set one polynomial's coefficient i dont want it to affect the others. How do i do this?
From the code you've shown, it looks like coefficients is probably a pointer initialized by the constructor Polynomial(int). From the behavior you describe, I'm certain enough of this that I'll answer your question even though you haven't shown the class.
Here's what it almost certainly looks like. Here I'll assume it stores int values:
class Polynomial {
public:
Polynomial() : coefficients(nullptr), size(0) {}
Polynomial( int s ) : coefficients(new int[s]), size(s) {}
~Polynomial() { delete [] coefficients; }
int * coefficients;
int size;
};
And so now here is the problem. You have violated the Rule of Three by not providing a copy or assignment constructor for a class that manages its own memory.
When you do this:
for (int r = 0; r < size; r++){
pArray[r] = Polynomial(p.size);
}
Here is what happens:
A temporary value is constructed by Polynomial(p.size)
The current pArray[r] value is destructed
The assignment operator is invoked, which by default will trivially copy data members from the temporary directly into pArray[r]
The temporary value from step 1 is destructed.
Note step 4. You copied the pointer into another object, and then deleted that pointer. Now you have undefined behavior. In your case, what probably happened is that the call to new always gave back the same pointer. That isn't guaranteed. Nothing is guaranteed when you have undefined behavior. But it does give away a strong hint that led me to write this answer.
To fix this, you must define a copy constructor for your object, which allocates a new coefficients array and copies the data into that.
Polynomial::Polynomial( const Polynomial & p )
: coefficients( new int [p.size] )
, size( p.size )
{
std::copy( p.coefficients, p.coefficients+size, coefficients );
}
Or even better, store your coefficients in a std::vector. You are using C++, after all. Then you don't need to worry about new, delete, copies, or moves (a concept which I have decided to omit from this answer).
If you must allocate, then at least store the pointer as a std::unique_ptr -- that will then immediately give you a compile error when you attempt to copy your object, and force you to make a non-trivial copy constructor.

How can I call a parameterized constructor inside a for loop.

I created a parameterizd constructor area. I used area to initialize the diagonal points. In the int main section, I am not able to call the constructor. Please correct my code and explain the mistakes:
int main()
{
int ab,p,q,r,s,l,m,n,o;
cout<<"Enter the number of rectangles: ";
cin>>ab;
for (int i=0; i<ab; i++)
{
cout<<"For rectangle "<<i+1<<endl;
cout<<"Enter the starting and ending values of the 1st diagonal: ";
cin>>p>>q>>r>>s;
cout<<"Enter the starting and ending values of the 2nd diagonal: ";
cin>>l>>m>>n>>o;
area obj[i](p,q,r,s,l,m,n,o);
obj[i].findArea();
obj[i].display();
}
return 0;
}
Just write :)
area obj(p,q,r,s,l,m,n,o);
obj.findArea();
obj.display();
As for the statement
area obj[i](p,q,r,s,l,m,n,o);
then you may not initialize arrays such a way. And it does not make sense to define an array within the loop.
Assuming the array obj should be used outside the loop I would suggest using a std::vector instead, declared before the loop. Then you have two alternatives:
Declare the vector and reserve enough memory (so the data doesn't have to be reallocated when adding new elements), then call emplace_back to add new elements.
std::vector<area> obj;
obj.reserve(ab);
for (int i=0; i<ab; i++)
{
...
obj.emplace_back(p,q,r,s,l,m,n,o);
}
Declare the vector with the correct size (ab) and have all elements default constructed, and then copy the actual object into place using simple assignment. This requires that area can be default constructed, and possible a copy-assignment or move-assignment operator.
std::vector<area> obj{ab};
for (int i=0; i<ab; i++)
{
...
obj[i] = area(p,q,r,s,l,m,n,o);
}
If you don't want an array (or a vector), and each object in the loop should just exist in the loop and nothing needs to be used after the loop, just declare the object with the correct arguments to the constructor: See the answer from Vlad from Moscow for how to do this.

Vector erase function deleting wrong object

I have a vector declared as:
vector<Curve> workingSet;
Curve is a class I've created that contains a string "name" and an array of structs, dynamically allocated by the constructor.
I have a loop that is supposed to delete 2 (out of 4) items from the vector.
vector<Curve>::iterator it = workingSet.begin();
//remove tbi's children from the working set
for ( ; it != workingSet.end();){
if (it->thisName == tbi.childAName ||it->thisName == tbi.childBName)
it= workingSet.erase(it);
else
++it;
}
When the debugger reaches the .erase(it) call I can see that the 'it' iterator is pointing to curve 2 in the vector. This is good; I want curve 2 to be removed from the vector.
I'm then taken by the debugger to the destructor (I have a breakpoint there), which presumably should be destructing curve 2. But when I look at the 'this' watch, I can see that the curve being destructed is curve 4! The destructor then proceeds to 'delete[]' the array in the object, as required and sets the array pointer to NULL.
When the debugger returns to the program, having completed the erase() call, I can see that vector 2 has been removed from the array and curve 4 is still there. Curve 4's array pointer still points to the same location as before, but the memory has been deallocated and the array contains garbage.
Can anyone suggest why curve 4 is being messed with?
N.b. (1) There is a copy constructor for the curve class, which does a 'deep' copy.
N.b. (2) There is more to the class/program than I've mentioned here
Btw, the array pointers in curves 2 and 4 point to different locations and contain different values, according to the debugger.
Edit: I've now implemented copy assignment. Now the correct item seems to be being erased from the vector, but the wrong destructor is still being called! However, when the debugger returns to the array curve 4 is still intact.
When an item is erased from the vector, all elements behind it are shifted towards the front to fill the empty space. If your compiler doesn't support move yet, it's done by copying all the elements, and the last item in the vector, which is now copied to the item before it, is a duplicate and is getting deleted.
At least that's how it should work.
It appears to me that vector::erase cannot be used with a vector of locally constructed non-trivial data types (objects not constructed on the heap, the proper name escapes me right now). I have the exact same behavior you describe, the last element gets destructed twice (especially dangerous if your object has memory that is freed by the destructor) and the element that you removed is never destructed. I do not know why this happens, but it is a pitfall to watch out for.
Here is one way to fix the problem:
#include <iostream>
#include <vector>
#include <memory>
using namespace std;
class MyClass
{
public:
int *x;
MyClass(int x)
{
cout << "MyClass Constructor " << x << endl;
this->x = new int(x);
}
MyClass(const MyClass& obj)
{
this->x = new int(*obj.x);
cout << "copy constructor " << *this->x << endl;
}
~MyClass()
{
cout << "MyClass Destructor " << *x << endl;
delete x;
}
};
int main(int argc, char* argv[])
{
// incorrect
vector<MyClass> bad_vect;
for(int i=0;i<3;i++){
bad_vect.push_back(MyClass(i));
// causes a bunch of copying to happen.
// std::move does not seem to fix this either
// but in the end everything gets constructed as we'd like
}
cout << " ---- " << endl;
bad_vect.erase(bad_vect.begin() + 1); // we expect this to remove item with x = 1 and destruct it.
// but it does NOT do that, it does remove the item with x=1 from the vector
// but it destructs the last item in the vector, with x=2, clearly
// not what we want. The destructor for object with x=1 never gets called
// and the destructor for the last item gets called twice.
// The first time with x=2 and since that memory is freed, the 2nd time
// it prints garbage. Strangely the double-free doesn't crash the prog
// but I wouldn't count on that all the time.
// Seems some parts of STL have pitfalls with locally constructed objects
cout << " ------------ " << endl;
// below fixes this
vector<unique_ptr<MyClass> >vect;
for(int i=0;i<3;i++){
unique_ptr<MyClass> ptr(new MyClass(i));
vect.push_back( move( ptr )); // move is required since unique_ptr can only have one owner
// or the single one-liner below
//vect.push_back( move( unique_ptr<MyClass>(new MyClass(i)) ));
}
// the above prints out MyClass Constructor 0,1,2, etc
vect.erase(vect.begin() + 1); // remove the 2nd element, ie with x=1
// the above prints out MyClass Destructor 1, which is what we want
for(auto& v : vect){
cout << *(v->x) << endl;
} // prints out 0 and 2
return 0; // since we're using smart pointers, the destructors for the
// remaining 2 objects are called. You could use regular pointers
// but you would have to manually delete everything. shared_ptr
// also works and you don't need the move(), but with a bit more overhead
}