Refer to the following code snippet.
According to my understanding:
a) 'p1' and 'p2' objects are created in the stack and get destroyed at the end of the getPoints() method.
b) When p1 and p2 are added to the vector using push_back(), the default Allocator creates new instances of Point and copy the values(x,y) of p1 and p2 into these newly created instances.
My questions are:
1) Is my understanding correct?
If so ;
2) If new Point objects are created by the Allocator, why I see only two lines of "Points created"?
Because I expect to see two lines for p1 and p2 and also two lines for newly created object by the Allocator.
3) How does the Allocator assign original values to x,y fields of the newly created objects? Does it using raw memory copy?
4) Is the shared pointer the recommended way to return a vector from a method?
#include <iostream>
#include <vector>
using namespace std;
struct Point {
Point() {
std::cout<< "Point created\n";
x=0;
y=0;
}
int x;
int y;
};
std::shared_ptr< vector<Point> > getPoints() {
std::shared_ptr< vector<Point> > ret = std::make_shared< vector<Point> >();
Point p1;
p1.x=100;
p1.y=200;
Point p2;
p2.x = 1000;
p2.y = 2000;
ret->push_back(p1);
ret->push_back(p2);
return ret;
}
int main(int argc, char** argv)
{
std::shared_ptr< vector<Point> > points = getPoints();
for(auto point : *(points.get())) {
std::cout << "Point x "<<point.x << " "<< point.y<<"\n";
}
}
Q: Is my understanding correct?
A: Your understanding is partially correct.
Q: If new Point objects are created by the Allocator, why I see only two lines of "Points created"?
A: New objects are not being created by the allocator - the allocator only allocates more memory, if needed . The objects that you insert in the vector are copy constructed. Because you have not created a copy constructor, the compiler has generated one for you.
Q: How does the Allocator assign original values to x,y fields of the newly created objects? Does it using raw memory copy?
A: As stated in the previous question, the allocator only allocates memory and does not create or destroy objects. The behavior of copying the fields is done by the copy constructor that gets invoked when you do a push_back
. The automatically generated copy constructor will do a member-wise copy construction of each of the class' members. In your case, x
and y
are primitive types, so they'll be just raw memory copied. If members were complex objects, their copy constructors would be invoked.
Q: Is the shared pointer the recommended way to return a vector from a method?
A: This would depend on your use case, and is opinion based. My personal advice, and this is for all kind of objects is:
std::vector<Point> getPoints()
) std::unique_ptr
. This applies to pretty much all factory functions that you might want to create. Even if you later want to share the ownership (see point 3), you can construct a shared_ptr by moving from a unique_ptr ( std::shared_ptr<T> shared = std::move(unique)
); shared_ptr
unless you really need shared ownership of the ptr. shared_ptr
are more complex to reason about, can create hard to debug cycles, leading to memory leaks, and are heavier in terms of performance (because of atomic operations relating to their refcount and additional allocated memory for a control block). If you think you need a shared_ptr
, reconsider your design and think if you can use a unique_ptr
instead. Internally, a std::vector is using memory allocated using the default allocator (or custom user provided, if you provided one) on the heap. This allocation happens behind the scenes, and is independent of the vector's size, and from the number of elements in the vector (but is always >= size()
). You can get how many elements the vector has allocated storage for by using the capacity()
function. When you call push_back()
, what happens:
capacity()
) to hold one more element, the argument that you passed to push_back is copy constructed , using the copy constructor if using the push_back( const T& value )
variant or moved from if using push_back( T&& value )
, by using the move constructor . reserve()
before inserting elements, to make sure that your vector will have enough capacity to hold at least as many elements without new allocations. After new memory has been allocated, the vector reallocates all existing elements into the new storage by either copying them, or moving them if they are not copy-insertable. This reallocation will invalidate all iterators and references to elements in the vector (caveat: the rules for when exactly copy vs move will be used when reallocating is a bit more complex, but this is the general case) Add a copy constructor to the Point
class to see what is happening.
Point(const Point& p) {
std::cout<< "Point copied\n";
this->x = p.x;
this->y = p.y;
}
You will see the statement printed five times if you are using GCC compiler. In the getPoints
function, once for the first push_back
, twice for the next push_back
because the vector is resized and all elements are inserted again. The fourth and the fifth time will be for the for
loop within main
.
You can eliminate three copies using reserve
to set the capacity of the vector
in the getPoints
function,
ret->reserve(2);
and by using references in the for
loop of the main
.
for(auto& point : *(points.get())) {
std::cout << "Point x "<<point.x << " "<< point.y<<"\n";
}
1) Is my understanding correct?
[Ans]Yes partially correct. Objects p1 and p2 are created in stack, but when pushed to vector it invoked copy constructor for creating and initializing new objects.
2) If new Point objects are created by the Allocator, why I see only two lines of "Points created"? Because I expect to see two lines for p1 and p2 and also two lines for newly created object by the Allocator.
[Ans]Using copy constructor. Please add a copy constructor and you will see the difference.
3) How does the Allocator assign original values to x,y fields of the newly created objects? Does it using raw memory copy? [Ans]Using copy constructor and vector itself is a dynamic array which reallocates memory as it needed.
4) Is the shared pointer the recommended way to return a vector from a method? [Ans] Depends on your use case as well. You can pass reference to a vector as a parameter and return the same.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.