I seem to be having a problem with swapping the elements of two vectors. I have two vectors, x and y that hold objects of type myclass. The myclass has only one public member w. I create a vector of pointers pointing to the members w of x and then swap the vectors x and y. I would expect the vector of pointers to still point to the w members of x but this does not seem to be the case.
Here is a trivial example to reproduce my problem.
#include <iostream>
#include <vector>
using namespace std;
struct myclass
{
double w;
};
int main()
{
vector<myclass> x(10);
for(int i=0; i!=10; i++) x[i].w = i;
for(auto el : x) std::cout << el.w << std::endl; /* prints i */
std::cout << std::endl;
vector<double *> px(10);
for(int i=0; i!=10; i++) px[i] = &x[i].w;
for(auto el : px) std::cout << *el << std::endl; /* prints i */
std::cout << std::endl;
vector<myclass> y(10);
for(int i=0; i!=10; i++) y[i].w = 2*i;
for(auto el : y) std::cout << el.w << std::endl; /* prints 2*i */
std::cout << std::endl;
y.swap(x);
for(auto &el : x) std::cout << &el.w << " " << el.w << std::endl; /* prints 2*i as it should */
std::cout << std::endl;
for(auto &el : px) std::cout << el << " " << *el << std::endl; /* should print 2*i, but prints i */
std::cout << std::endl;
}
Notice that x and y have swapped elements but px stills points to the old elements. I read that using swap is not supposed to invalidate pointers/iterators. Is this correct or am I missing something?
Thanks in advance!
The pointers and iterators are not invalidated, but they follow the contents of the container.
The contents of x are swapped into y, but iterators and pointers to these values will keep pointing at them (even though they are now in y).
Think about it, how could it work in any other way? If two containers of unequal length were swapped, what would pointers to elements near the end of the longer container point to in the shorter container? How would it be possible to implement swap() in O(1) if the elements of each container had to be moved in memory to ensure that pointers remained valid?
Related
I am new to C++ and I really need some help on this. I am trying to create a structure to interface with the GSL Monte-Carlo algorithms (a fact that is really not important for this example). I have searched all of the C++ tutorials, the stackoverflow posts and the GSL documentation with no luck. I am using the armadillo package for matrix manipulation; it is very robust. I am unable to use a dynamic array within the structure, as per the documentation, so I am trying to find a way to make my structure variable *M point to the values in my array *L[]. I am sure that this would be better with a vector but 1) the rest of the code (in bad form) uses pointers already, and 2) I am looking at this as a learning experience. I am surprised that the addresses for *M and *L[] are not the same in my code. I am also, less importantly, surprised that my std::cout prints a different number of spaces for each line. The code exits before printing the last std::cout as shown in the output below.
Thanks for your help!
#include "pch.h"
#include "stdio.h"
#include "complex"
#include "new"
#include "armadillo"
using namespace arma;
class Link
{
public:
arma::Mat<cx_double>::fixed<3, 3>* dir[4]; // pointer to directional SU(3) matrices
Link(); // default constructor
};
Link::Link() // default constructor - all directional matrices are the identity
{
for (size_t hcount = 0; hcount < 4; hcount++)
{
dir[hcount] = new arma::Mat<cx_double>::fixed<3, 3>{ fill::eye }; // create directional matrix in direction hcount
}
}
struct Param
{
Link* M;
};
int main()
{
const int size = 10;
Param* Parameters = new Param{ NULL };
Link* L[size];
arma::Mat<cx_double>::fixed<3, 3> One{ fill::eye };
for (size_t hcount = 0; hcount < 10; hcount++)
{
L[hcount] = new Link();
*L[hcount]->dir[1] = *L[hcount]->dir[1] + hcount * One; // Make each array element #1 unique
}
Parameters->M = L[0];
std::cout << "&L = " << &L << std::endl;
std::cout << "&Parameters->M = " << &Parameters->M << std::endl; // surprised that addresses are not the same
std::cout << std::endl;
std::cout << "&L[0] = " << &L[0] << std::endl;
std::cout << "&Parameters->M[0] = " << &Parameters->M[0] << std::endl;
std::cout << std::endl;
std::cout << "&L[5] = " << &L[5] << std::endl;
std::cout << "&Parameters->M[5] = " << &Parameters->M[5] << std::endl;
std::cout << std::endl;
std::cout << "&L[5]->dir[1] = " << &L[5]->dir[1] << std::endl;
std::cout << "&Parameters->M[5].dir[1] = " << &Parameters->M[5].dir[1] << std::endl;
std::cout << std::endl;
std::cout << "*L[5]->dir[1] = " << *L[5]->dir[1] << std::endl; // This works
std::cout << "*Parameters->M[5].dir[1] = " << *Parameters->M[5].dir[1] << std::endl; // This does not
std::cout << std::endl;
}
OUTPUT
&L = 0024F7CC
&Parameters->M = 004EEFD8
&L[0] = 0024F7CC
&Parameters->M[0] = 004E0578
&L[5] = 0024F7E0
&Parameters->M[5] = 004E05C8
&L[5]->dir[1] = 004E50C4
&Parameters->M[5].dir[1] = 004E05CC
*L[5]->dir[1] = (+6.000e+00,+0.000e+00) (0,0) (0,0)
(0,0) (+6.000e+00,+0.000e+00) (0,0)
(0,0) (0,0) (+6.000e+00,+0.000e+00)
*Parameters->M[5].dir[1] =
&L is the adress of L, so it's the adress of the pointer to the first element not the adress of the first elemenr itself. Same for & Parameters->M. That is the adress of thd the Member M from Parameters. You want to compare L[0] with Parameters->M except when M should not point to the element that L[0] refers to but to the start of the array itself, then you want to compare it with L. But then you also have to change the assignment.
I find it a bit weird that you use an array of pointers. Just use an array of Links.
#include <iostream>
#include <vector>
//#include <string>
struct Point {
Point(int _x, int _y) {
x = _x;
y = _y;
}
int x;
int y;
Point *parent;
};
int main() {
Point start(3, 4);
std::vector<Point> points;
points.push_back(start);
std::cout << points.back().x << "," << points.back().y << "\n";
Point one(4, 5);
one.parent = &points.at(0);
//std::cout << "testing: " << one.parent->x << "," << one.parent->y << "\n";
points.push_back(one);
std::cout << "One: " << points[1].x << "," << points[1].y << "\n";
std::cout << "One's parents: " << points[1].parent->x << "," << points[1].parent->y << "\n";
Point two(10, 3);
two.parent = &points.back();
points.push_back(two);
std::cout << "Two: " << points[2].x << "," << points[2].y << "\n";
std::cout << "Two's parent: " << points[2].parent->x << "," << points[2].parent->y << "\n";
Point three(12, 7);
three.parent = &points[1];
points.push_back(three);
std::cout << "Three: " << points[3].x << "," << points[3].y << "\n";
std::cout << "Three's parents: " << points[3].parent->x << "," << points[3].parent->y << "\n";
return 1;
}
I get the following results:
3,4
One: 4,5
One's parents: 0,0
Two: 10,3
Two's parent: 4,5
Three: 12,7
Three's parents: 4,5
Even though I made one's parent point to the vector's first element, the value ends up being 0,0. But, the other pointers point to the element that I want it to.
std::vector has a capacity. If you add elements beyond the vector's current capacity, vector may decide to allocate a larger block, move the existing elements, and then add the new elements. This must have happened in your case too.
You can bump the capacity of an existing vector with reserve. This will not yet add extra elements; it just prepares the vector.
While MSalters correctly explained the reason of the issue, and the possible solution for this particular case, the general practice is a bit different. Because vector may be reallocated at any moment, it's generally bad idea to store pointers to its elements.
You may use indices instead, which will be valid regardless of the re-allocation; or you may consider using different data structure, such as std::list.
Elements of std::list remain in the same location all their lifetime.
Why doesn't the range-based loop with auto display addresses?
The for loop:
for (int i = 0; i < s; i++) cout << &ar[i] << endl;
works normally, but range-based loop with auto doesn't:
#include <iostream>
#include <algorithm>
using namespace std;
int main() {
int ar[] = { 12,-23,0,1,2 };
int s = sizeof(ar) / sizeof(int);
int * m = &ar[0];
sort(&ar[0], m+ s);
for (auto m : ar)
cout << m << endl;
cout << endl;
for (auto m : ar)
cout << &m << endl;
cout << endl;
for (int i = 0; i < s; i++)
cout << &ar[i] << endl;
system("pause");
}
With the auto m you are passing (array) elements by value / copy in your range based loop:
for (auto m : ar) { // pass by value
std::cout << &m << ' '; // prints addresses of copies, not the actual array elements
}
This means m becomes a copy of an array element in each iteration and has its own address in the memory.
If you passed by reference (auto& m) or a reference to const (const auto& m), you would observe the expected results:
for (auto& m : ar) { // pass by reference
std::cout << &m << ' '; // prints addresses of the actual array elements
}
Now m is an actual array element and &m represents the array element address as expected.
Vectors double their size each time they run out of space when adding an element, but what about when you remove elements? say you added 800 elements to an array, and on the addition of that 800th element, the vector doubles its size to be able to hold 1600 elements. Now what if you start taking away elements to the point that its only holding say 5 or 10 elements?
will it recognize that the vector is much smaller than half the size of the space reserved for future elements and reserve less space?
Vectors do not decrease in capacity when removing elements! This is to allow future elements to be added efficiently into the existing buffer.
If it has already allocated a block of memory, it will continue to use it because it would be inefficient for it to free up some memory and later find it has to allocate more memory.
I always recommend writing a test snippet of code to test these sorts of things.
For example, I threw this together in 2 minutes to verify that I was telling you correct information:
#include <iostream>
#include <vector>
void printInfo(std::vector<char> &_vector)
{
std::cout << "Size: " << _vector.size() << std::endl;
std::cout << "Capacity: " << _vector.capacity() << std::endl;
std::cout << std::endl;
}
int main()
{
int numbElems = 10;
std::vector<char> myvector;
std::cout << "Nothing entered" << std::endl;
printInfo(myvector);
for (int i = 0; i < 10; i++) {
for (int c = 0; c < numbElems; c++) {
myvector.push_back(i);
}
std::cout << "Pushed " << numbElems << std::endl;
printInfo(myvector);
}
for (int i = 0; i < 5; i++) {
for (int c = 0; c < numbElems; c++) {
myvector.pop_back();
}
std::cout << "Popped " << numbElems << std::endl;
printInfo(myvector);
}
myvector.erase(myvector.begin(), myvector.end());
printInfo(myvector);
std::cout << "max_size: " << myvector.max_size() << std::endl;
return 0;
}
If you compile and run you will see Capacity never goes down in size. Even after erase, or some of the elements are removed.
On linux you can use less to scroll through the output.
Having a class members std::vector<double> v and int n, what is the difference between using the following on this vector, which is not initialized:
v.assign(n, 0.0);
or
v.resize(n, 0.0);
assign sets the size to n and all element values to 0.0, whereas resize sets the size to n and only new element values to 0.0.
If v is empty beforehand, they're the same, but assign is probably clearer.
I guess by not initialized, you mean it's default initialized, i.e, an empty vector, then
v.assign(n, 0.0);
and
v.resize(n, 0.0);
both changes the size of the vector to n and all the elements to 0.0. Note that for non-empty vectors, they are usually not the same, resize() only sets the new elements to 0.0.
Assign means replacing a vector with new properties (size and elements). Resize means holding old data and expanding the new vector with new elements, this in case the new size is greater than the old one, otherwise shrink the size and eliminate the extra.
Run the following code twice. One for assign the second one for resize (just uncomment the first one).
#include <iostream>
#include <vector>
int main ()
{
std::vector<int> vec1;
vec1.assign(7,100);
// vec1.resize(7, 100);
std::cout << "Size: " << vec1.size() << std::endl;
for (unsigned int i(0); i < vec1.size(); ++i)
{
std::cout << vec1[i] << std::endl;
}
vec1.resize(4,5);
// vec1.assign(4,5);
std::cout << "\nSize: " << vec1.size() << std::endl;
for (unsigned int i(0); i < vec1.size(); ++i)
{
std::cout << vec1[i] << std::endl;
}
vec1.resize(10,5);
// vec1.assign(10,5);
std::cout << "\nSize: " << vec1.size() << std::endl;
for (unsigned int i(0); i < vec1.size(); ++i)
{
std::cout << vec1[i] << std::endl;
}
std::cin.get();
return 0;
}