Is Std::Vector Copying the Objects with a Push_Back

Is std::vector copying the objects with a push_back?

Yes, std::vector<T>::push_back() creates a copy of the argument and stores it in the vector. If you want to store pointers to objects in your vector, create a std::vector<whatever*> instead of std::vector<whatever>.

However, you need to make sure that the objects referenced by the pointers remain valid while the vector holds a reference to them (smart pointers utilizing the RAII idiom solve the problem).

Does std::vector's push_back create a deep copy of the argument?

Here's a little testing program to demonstrate the data-sharing properties of cv::Mat objects (which are matrix headers) in special!

int main()
{
// create input of size 512x512
cv::Mat input = cv::imread("../inputData/Lenna.png");

// create a second input of size 256x256
cv::Mat modifiedInput;
cv::resize(input, modifiedInput, cv::Size(256,256));

std::vector<cv::Mat> images;

// first element will be a "deep copy" where the matrix elements will be copied to a new memory location and a new header will be created, referecing those matrix elements.
images.push_back(input.clone());

// 6 times copy the "input" to "images".
// All the copies will (deep) copy the matrix header but they will share the matrix elements (because their memory LOCATION will be copied)
for(unsigned int i=0; i<6; ++i)
images.push_back(input);

// now some experiments:
// draw a circle to input variable. At this point it should share it's matrix elements with images[1-5]
cv::circle(input, cv::Point(100,100), 30, cv::Scalar(0,0,0), -1);

// draw a circle to a vector element:
cv::circle(images[5], cv::Point(300,100), 30, cv::Scalar(0,0,0), -1);

// use a openCV function that will allocate new memory, if the destination dimensions don't fit:
// to a mat whose dimensions fit:
// remember that input.size() == vector[0..5].size
// compute median blur and target one of the matrices that share their data at the moment:
cv::medianBlur(input, images[3], 11);

cv::imshow("0", images[0]);
cv::imshow("1", images[1]);
cv::imshow("2", images[2]);
cv::imshow("3", images[3]);
cv::imshow("4", images[4]);
cv::imshow("5", images[5]);
cv::waitKey(0);

At this time it's looking like this: All matrices share their element's data except the first matrix because there a deep-copy was forced with .clone().

Sample Image

now continue with this:

    // to a mat whose dimensions don't fit (new memory will be allocated, not shared by the other matrix headers anymore):
// images[3] will not share the data with other matrix headers afterwards
cv::medianBlur(modifiedInput, images[3], 11);

// now images[3] and images[4] will share matrix elements
images[4] = images[3];
cv::circle(images[4], cv::Point(128,128), 20, cv::Scalar(255,255,255), 3);

// create a deep-copy of 256x256 input to overwrite images[5] (not modifying any other image's matrix elements)
images[5] = modifiedInput.clone();
cv::circle(images[5], cv::Point(0,0), 30, cv::Scalar(0,255,0), -1);

cv::imshow("0", images[0]);
cv::imshow("1", images[1]);
cv::imshow("2", images[2]);
cv::imshow("3", images[3]);
cv::imshow("4", images[4]);
cv::imshow("5", images[5]);

//cv::imshow("input", input);
//cv::imwrite("../outputData/MainBase.png", input);
cv::waitKey(0);
return 0;
}

looks like this:
Sample Image

This time, the call of medianBlur did NOT share the data with all the other matrices, because the destination-image's dimensions DID NOT FIT, so new memory had to be allocated for images[3] within the medianBlur method. So images[3] referenced different data elements aferwards!

All this might be a little tricky because the user might not see directly, which function calls will allocate new data and which ones don't, so if you want to be sure to allocate new data, you should do this in beginning for each mat, or use an empty mat as destination (or don't share any data in the beginning).

One more thing:

cv::Mat emptyMat;
std::vector<cv::Mat> images(n, emptyMat); // insert n copies of emptyMat header
// or
for(unsigned int i=0; i<n; ++i)
images.push_back(emptyMat) // same result

this is both save to use so that not data is shared, because all emptyMat doesn't have any data in the beginning, so no data can be shared. Whenever any data is assigned to any of the vector elements, the other's don't know about it and so they won't share that data.

// BUT:
cv::Mat notEmptyMat = cv::Mat::zeros(height, width, type);
std::vector<cv::Mat> images(n, notEmptyMat ); // insert n copies of emptyMat header which references the assigned zeroes data of size width x height
// or
for(unsigned int i=0; i<n; ++i)
images.push_back(notEmptyMat ) // same result

Here, data is shared and whenever you change the DATA of one of those matrices, the other ones will be changed, too. But obviously, if you assign new data memory to one of those matrices, the other ones still reference their other data memory.

Unable to avoid copying while pushing objects with copy-construcor into a vector

std::move isn't useful here. The temporary Vertex object is already a prvalue, so casting it to an xvalue doesn't change anything. The class has no move constructor, so copy initialisation cannot move; it has to copy. The implicit move constructor has been inhibited by the user defined copy constructor. Although, the move constructor couldn't be any faster than the copy constructor for this class anyway.

The way that emplace_back works is that it forwards the arguments to the constructor of the element. If the argument that you pass is an object of the element type, then you invoke the constructor that accepts another instance of the class - that is the copy constructor (or the move constructor for classes that have it).

Instead of creating a temporary Vertex object, and passing it as an argument to emplace_back, you should pass the three size_t objects that can be forwarded to the constructor Vertex(size_t, size_t, size_t). This way you can avoid copying (and moving) of the Vertex object entirely:

vert.emplace_back(1, 2, 3);
vert.emplace_back(4, 5, 6);
vert.emplace_back(7, 8, 9);

How can I push_back/copy custom type ( array) object to vector?

std::vector<foo> means "vector of foo values", you can't push_back(new foo) into it as new foo is a pointer (foo*). You'd need to push_back(foo{}) which default-constructs a foo instance (a value). This doesn't work because:

  • std::vector<T>::push_back requires that T is MoveInsertable (in this particular case)
  • foo is a C-array, and C-arrays are not MoveInsertable

In general, C-arrays don't work well with std::vector. You can use std::array instead, because it is MoveInsertable.

#include <vector>
#include <array>
#include <iostream>

using foo = std::array<std::array<std::pair<float, float>, 8>, 8>;

std::vector<foo> v;
v.push_back(foo{});
std::cout << v[0][0][0].first << "\n";

In this case you don't need your copy function as std::array will do copying

Live Demo

c++ push_back copying object instead of reference

Nothing in your code stores references. Your code copies a node because you allocate a new node in std::make_shared and invoke the copy constructor.

  std::vector<Node> nodes;

It's local to your function. It would be impossible to keep either pointers (shared or not) or references to elements of this vector. Perhaps you want to use a vector of shared pointers instead:

 std::vector<std::shared_ptr<Node>> nodes;

Bear in mind that shared_ptr doesn't work well in presence of cyclic referenced. If your data structure is a general graph, then perhaps shared_ptr is not appropriate for storing neighbours. You may want to consider weak_ptr instead (you will have to keep a container of shared pointers to all nodes separately).

How to push_back a class object into a std::vector?

"however when i try to push_back to a vector in MAIN function, it
return 0 0 0 (B default values)"

This is because of not initializing the member variables of B class. This should be done when you push_back the a new A object to the std::vector like follows:

vecA.push_back(A("name", "path", B(15, 04, 2018)));

If your doubt is how to to use push_back is, above will certainly clarified it.

Update: I have set the copy constructor and copy assignment operator to default and it worked. Live action: https://www.ideone.com/TlmAm2

#include <iostream>
#include <string>
#include <vector>

class Date
{
public:
Date(int day = 0, int month = 0, int year = 0)
: _day(day), _month(month),_year(year) {}
~Date(){}

int get_day() { return _day; }
int get_month() { return _month; }
int get_year() { return _year; }
void writestuff()
{
std::cout << _day << "/" << _month << "/" << _year<< std::endl;
}
private:
int _day;
int _month;
int _year;
};

class Adatok
{
public:
Adatok(std::string name, std::string path, Date date )
: _name(name), _path(path), _date(date) {}
~Adatok(){}
void writestuff()
{
std::cout<<_name<<" "<<_path<<" ";
_date.writestuff();
std::cout<<std::endl;
}
//change in copy constructor and copy assignment operator
Adatok(const Adatok& other) = default;
Adatok& operator= (const Adatok& other) = default;
private:
std::string _name;
std::string _path;
Date _date;
};

void database(std::string temp, std::vector<Adatok> my_vec)
{
for(auto& it: my_vec)
it.writestuff();
}
int main(int argc, char **argv)
{
std::vector<Adatok> my_vec;
int year = 2018, month = 04, day = 15;
std::string name = "name1", path = "path1";
my_vec.push_back(Adatok(name,path,Date(day,month,year)));

database("something", my_vec);

return 0;
}

When std::vector push_back() the object which has the reference member

You're getting this behavior because you are initializing your 'ref' member to a value that is on the stack, then the default copy constructor is copying that into the vector.

For example, in my debugger the value I have for ref is:

+       &myVector[1].ref    0x00eff80c {2.0000000000000000} double *
+ &myVector[1] 0x00126940 {a=2.0000000000000000 ref=2.0000000000000000 }
+ myVector { size=0x00000002 } std::vector<myClass,std::allocator<myClass> >
+ &nIter 0x00eff8f0 {0xffffffff} int *

You can see that myVector[1].ref is not inside myVector[1] as you'd expect, and is, in fact, on the stack. You can see that nIter and ref are only 57 bytes apart:

&nIter - (int*)&myVector[0].ref 57

If you want to see how this is implicitly happening you can delete your copy constructor:

myClass(myClass const &rhs) = delete;

inside myClass and you'll get an error at push_back.

Another option is to write your own copy constructor:

myClass(myClass const &rhs) : ref(a) {
a = rhs.a;
}

If you debug this you'll see that the values are correct, and that the memory locations of each ref are now inside the bounds of the myClass objects.

Finally you might be able to use emplace_back instead of push_back, which will construct myClass directly in the vector's memory instead of calling the copy ctor, though I wouldn't recommend this as it leaves this ref copy bug.

also don't forget the assignment operator if you go the copy ctor route:
https://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)

e.g.

myClass baz;
baz = myVector[0];

this will invoke operator= and not the copy ctor. My compiler (visual studio c++ latest) automatically deletes operator= if you declare a copy ctor, so it would catch this, but your compiler may not.

vector::push_back using std::move for internal reallocations

According to [vector.modifiers]/2 if the element type of std::vector is not copy-insertable and not nothrow-move-constructible, then an exception thrown from a move constructor of the element type results in an unspecified state of the container.

std::vector must prefer the copy constructor if the type isn't nothrow-movable, so that the exception guarantees can be preserved, but if that isn't possible, it is impossible to give strong exception guarantees.

push_back to std::vector, the copy constructor is repeatedly called

First of all you have to remember that unless you reserve memory for the vector, it needs to allocate and reallocate memory as its capacity needs to increase.

The reallocation basically allocates new memory, then copies the element in the vector to the new memory.

Furthermore, when you push a value into a vector, that value needs to be copied into the vector. You can avoid that by emplacing the value (which means it's constructed in place in the vector), or by moving the value when you push it back.



Related Topics



Leave a reply



Submit