I have the following vector-to-vector copy.
vec_a = vec_b;
Here, vec_a and vec_b holds vec_structurename elements.
Structure definition is something like this.
typedef struct{
uint16 name;
other_strctuname info;
}vec_structurename;
My question is when we do vector-to-vector copy here. What's the expectation?. Will it call a single memcpy or will it call vec_b.size() number of memcpy implicitly?
It's closer to this. Or more appropriately, this is how you should think about it:
// vec_a = vec_b
//
vec_a.clear();
vec_a.reserve(vec_b.size()); // OR vec_a.reserve(vec_b.capacity())
for (size_t i = 0; i < vec_b.size(); i++) {
vec_b.push_back(vec_b[i]);
}
However, the compiler is free to optimize a vector copy with a memory allocation for vec_a followed by a memcpy like copy for plain-old-value types and trivially copyable structs.
Otherwise, when each push_back call is invoking the copy constructor of each
vec_a[i] = vec_structurename(vec_b[i]);
Without an explicitly copy constructor, it will be a member by member assignment:
Which in the case of your data structure, is mostly this:
vec_a[i].name = vec_b[i].name;
vec_a[i].other_strctuname = vec_b[i].other_strctuname;
Same goes for the assignment of other_strctuname. It will either make use of the copy constructor of that class or fallback to member by member assignment.
Again, if everything is trivially copyable, the compiler is free to do a quick allocation and memcpy.
Here's a Godbolt example using your class as an example with different types. For the trivially copyable classes, you can see the new + memcpy getting invoked within the inline assembly. For the struct that has a string, it has to do something else.
https://www.godbolt.org/z/o973PeGx1
just call a single memcpy,because the source is the elements are stored in a continuous block of memory,just easy copy.
Related
When manage your own linear memory, you will encounter the need to call realloc function to expand your memory. But when the destructor of the string object is called in the new memory space, it will crash. But if it is another type such as vector, there will be no problem. Excuse me, why is this? The code is as follows
#include<iostream>
#include<string>
#include<vector>
#include <cstdlib>
using namespace std;
int main() {
char *p = (char *)malloc(512);
// error code
new(p) string("foo");
cout << *reinterpret_cast<string *>(p) << endl;
p = (char *)realloc(p, 1024);
string *k = reinterpret_cast<string *>(p);
k->~string();
// ok code
//new(p) vector<string>(2, "foo");
//cout << (*reinterpret_cast<vector<string> *>(p))[0] << endl;
//p = (char *)realloc(p, 1024);
//vector<string> *k = reinterpret_cast<vector<string>*>(p);
//k->~vector<string>();
free(p);
return 0;
}
I know that STL usually allocates new memory, and then move the old objects to the new memory. But the memory I manage can store many types, so I need to call the constructor and destructor of the corresponding type when expanding. The code writing is a bit complicated, so I want to use realloc to be lazy.
What you describe does not work with strings, but will generally not work with most c++ objects. Even when it appears to work with std::vector it still is undefined behavior, and your expectations of "no problem" are unwarranted. UB may result in ways that look correct, but you cannot rely on that, or expect it to continue to work in the future.
For any type you use in your memory scheme described, such as std::string, the following must compile:
#include <type_traits>
static_assert(std::is_trivial_v<std::string>);
If the above doesn't compile, it is not safe to use in your memory scheme.
Think of trivial types as a c-style struct. They don't do anything, but only hold simple data, or compositions of simple data, and are safe to move around with memcpy.
The requirements on what is trivial are as follows, from cppreference:
The following types are collectively called trivially copyable types:
Scalar types
Trivially copyable classes, i.e. classes satisfying following requirements:
At least one copy constructor, move constructor, copy assignment operator, or move assignment operator is eligible
Every eligible copy constructor (if any) is trivial
Every eligible move constructor (if any) is trivial
Every eligible copy assignment operator (if any) is trivial
Every eligible move assignment operator (if any) is trivial
Has a trivial non-deleted destructor
Arrays of TriviallyCopyable objects
This implies that a trivially copyable class has no virtual functions or virtual base classes.
A new type trait, is_trivially_relocatable may exist in the future, and be a more specific test and a few more types will be eligible but it does not exist in c++ today.
So why does std::string fail? Every one of the above requirements for triviality fails. It manages its own memory internally, and so its copy and move functionality is not trivial. The problem is easily visible because strings have the Small Buffer Optimization, which means it may store pointers internally to itself, and moving them via memcpy fails to update its internal state to its new location.
What makes a copy constructor non-trivial? Again from cppreference:
The copy constructor for class T is trivial if all of the following are true:
it is not user-provided (that is, it is implicitly-defined or defaulted) ;
T has no virtual member functions;
T has no virtual base classes;
the copy constructor selected for every direct base of T is trivial;
the copy constructor selected for every non-static class type (or array of class type) member of T is trivial;
In short, you really are very limited in what you can use in your highly error-prone approach, and you probably should consider something entirely different.
I've an operator and function which goal is to copy the values of the vector pointer to a vector. I've read that as long it's a vector pointer, deep copying methods would only copy its pointer, not its value. I'm wondering how to copy it's values over instead. (The vector is a member of CObject class)
Operator function:
void CObject::operator=(CObject& rhs)
{
this->ClearObject(); //Object will be cleared first
// How to perform the deep copy below?
copy(rhs.m_Vector.begin(), rhs.m_Vector.end(), back_inserter(this->m_Vector));
}
Declared as -> void operator=(CObject& rhs);
Example application of operator function (Performed in another class):
CObject* m_pObjectOne;
CObject m_ObjectTwo;
m_ObjectTwo = m_pObjectOne;
(Therefore, when m_pObjectOne is deleted in its class destructor, m_ObjectTwo loses its value)
Vector & others:
struct OBJECT_ITEM
{
char m_chType;
UINT m_nDataByte;
BYTE* m_pData;
CString m_strRecipeTag;
}
std::vector<OBJECT_ITEM> m_Vector;
I've also tried other deep copy methods like push_back() & assign() but it gives me the same result.
Any help would be appreciated!
Strive for the "rule of zero".
How to Deep Copy Values of Vector Pointer (Not just the pointer)?
Basically - don't. You should strive to follow the rule of zero: Unless otherwise necessary, set things up so that the default constructors, assignment operators and destructors do what they should.
In your case: Suppose you let CObject use its default (copy) assignment operator. That means, that rhs.m_Vector will be assigned to lhs.m_Vector. And vector assignment means that individual members of the vector are assigned to their corresponding members.
The way you wrote your vector, that won't do what you want: You wrote you want to avoid the case of "when m_pObjectOne is deleted in its class destructor, m_ObjectTwo loses its value)". Well, since you're willing to hold copies of the objects, consider something like the following:
struct OBJECT_ITEM
{
char m_chType;
UINT m_nDataByte;
MyContainer m_upData;
CString m_strRecipeTag;
}
And choose or write MyContainer to suit your needs. It could just be an std::vector<BYTE>; or if you don't want the size to change after construction, use a dynarray (not in the standard library these days; here's an alternative); etc. And your chosen container will actually get properly copied when you copy an OBJECT_ITEM. Finally, don't hold pointers to OBJECT_ITEM's - just hold actual OBJECT_ITEM's (which it seems you were already doing?)
And there, now you can no longer have dangling pointers - with no custom constructors, assignment operators or anything else like that.
Edit: I originally posed this question out of context so I've reworked it. I've left as much as possible unchanged so most of your responses will still apply.
I'm having trouble understanding how to implement a constructor which accepts a pointer to an array of pointers.
I have the following class which contains a member, bodies, of type Body** (i.e. it is a pointer to an array of pointers to body objects).
class Galaxy
{
private:
int n; // Number of bodies in galaxy.
Body** bodies; // Ptr to arr of ptrs to Body objects.
public:
Galaxy();
Galaxy(int, Body**);
// Some other member functions.
};
Here is the implementation of the constructors:
// Default constructor. Initializes bodies to null pointer.
Galaxy::Galaxy() : bodies(NULL) {}
// Alternate constructor. Here I try to perform a deep copy of bodiesIn.
Galaxy::Galaxy(int nIn, Body** bodiesIn)
{
n = nIn;
// Allocate memory for an array of n pointers to Body objects.
bodies = new Body*[n];
// Perform deep copy.
for (int i=0; i<n; i++)
{
bodies[i] = new Body;
*bodies[i] = *bodiesIn[i];
}
}
Is this method sound, or is there a preferred way to construct such an object.
P.S. I realize it would be easier to code this with std::vector's, however the size of the array doesn't change, and minimizing memory usage is important.
There's lots wrong with your function:
Creating an object and then immediately assigning to it is inefficient, use the copy ctor instead.
If an exception is thrown by any new-expression but the first one or by one of the assignments, you are leaking objects.
Better take a std::size_t for the size, it's designed for it.
Better swap the arguments, that's more idiomatic.
You don't return the copy at the moment
Why not templatize it?
BTW: std::unique_ptr does not add any overhead, but provides plenty of comfort and safety.
According to N3485 ยง23.3.2.2:
(...) the implicit move constructor and move assignment operator for array require that T be MoveConstructible or MoveAssignable, respectively.
So, std::array supports move semantics if the type of its elements does. Great!
However, what does this really mean? I tend to picture this type as a safer version of an array providing an STL-compliant interface but, if this is true, then how can an std::array move-construct its elements? Can I do the same with an ordinary array?
However, what does this really mean?
It means that, if the element type is movable, then so is the array type.
std::array<movable, 42> move_from = {...};
std::array<movable, 42> move_to = std::move(move_from); // moves all the elements
I tend to picture this type as a safer version of an array providing an STL-compliant interface
Not really. It's a wrapper for an array, giving it the same semantics as an aggregate class - including the ability to copy and move it.
how can an std::array move-construct its elements?
In exactly the same way as any other aggregate. Its implicit move-constructor will move-construct all its members, including the elements of any member arrays.
Can I do the same with an ordinary array?
Only if you wrap it in a class type, as std::array does.
Moving a std::array is different from moving a std::vector. When moving one std::vector into another, it's (sometimes*) possible to simply re-target the internal pointers and avoid manipulating the elements at all.
With std::array, this is of course not possible - its elements have automatic storage duration, they are literally contained inside the object. However, each individual one of them can still be moved, and that's what the move operations on std::array do**.
* Assuming the allocators are compatible and don't prohibit this operation
** That's also what you get with std::vector when the buffer can't just be re-owned by the destination vector.
The default move constructor for a (non-union) class performs a member-wise move. Moving a raw array data member means moving each of the array's elements, see [class.copy]/15.
Therefore, you can move a raw array by putting it inside a class:
struct wrap
{
std::string arr[25];
};
auto w = wrap();
auto m = std::move(w); // moves the 25 `std::string`s
You can also manually invoke the move constructor of the elements, for example:
std::string a[3] = { /*...*/ };
std::string b[3] = {std::move(a[0]), std::move(a[1]), std::move(a[2])};
It is not specified if std::array contains a raw array. However, it does contain data members of the value_type, since it's guaranteed to be an aggregate. Those data members will be moved as described above when invoking the move constructor.
If the data members of a std::array are not MoveConstructible, instantiating its move constructor will fail.
You can do it using "placement new". You'll find plenty of questions on placement new already answered with many further details.
This one looks like it has a complete example:
Why is this code trying to call the copy constructor?
For some reason, I want to return an object of my::Vector (which is basically a wrapper class that internally use STL vector for actual storage plus do provide some extra functions). I return vector by value as the function creates a vector locally every time.
my::Vector<int> calcOnCPU()
{
my::Vector<int> v....
return v;
}
Now I can have multiple nesting of function calls (considering a library design), so in short something like following:
my::Vector<int> calc()
{
if(...)
return calcOnCPU();
}
AFAIK, returning by value would invoke copy constructor of my::Vector class which is something:
Vector<int>::Vector(const Vector& c)
{
....
m_vec = c.m_vec; // where m_vec is std::vector<int>
}
Few questions:
1) In copy constructor, is it invoking copy constructor of std::vector? or assignment operator and Just to confirm, std::vector creates deep copy (meaning copies all elements considering basic integer type).
2) With nesting of calcOnCPU() in calc() each returning Vector of int: 2 or 1 copies of Vector will be created? How could I avoid multiple copies in case of such simple method nesting? Inline functions or there exist another way?
UPDATE 1: It became apparent to me that I need to keep my own copy constructor as there are some custom requirements. However, I did a simple test in main function:
int main() {
...
my::Vector v = calc();
std::cout<<v;
}
I put some prints using "std::cerr" in my copy constructor to see when it gets called. Interestingly, it is not called even once for above program (atleast nothing gets printed). Is it copy ellision optimization? I am using GNU C++ compiler (g++) v4.6.3 on Linux.
In copy constructor, is it invoking copy constructor of std::vector? or assignment operator
In your case, it's creating an empty std::vector, then copy-assigning it. Using an initialiser list would copy-construct it directly, which is neater and possibly more efficient:
Vector<int>::Vector(const Vector& c) : m_vec(c.m_vec) {
....
}
Just to confirm, std::vector creates deep copy
Yes, copying a std::vector will allocate a new block of memory and copy all the elements into that.
With nesting of calcOnCPU() in calc() each returning Vector of int: 2 or 1 copies of Vector will be created?
That's up to the compiler. It should apply the "return value optimisation" (a special case of copy elision), in which case it won't create a local object and return a copy, but will create it directly in the space allocated for the returned object. There are some cases where this can't be done - if you have multiple return statements that might return one of several local objects, for example.
Also, a modern compiler will also support move semantics where, even if the copy can't be elided, the vector's contents will be moved to the returned object rather than copied; that is, they will be transferred quickly by setting the vector's internal pointers, with no memory allocation or copying of elements. However, since you're wrapping the vector in your own class, and you've declared a copy constructor, you'll have to give that class a move constructor in order for that to work - which you can only do if you're using C++11.
How could I avoid multiple copies in case of such simple method nesting? Inline functions or there exist another way?
Make sure the structure of your function is simple enough for copy elision to work. If you can, give your class a move constructor that moves the vector (or remove the copy constructor, and the assignment operator if there is one, to allow one to be implicitly generated). Inlining is unlikely to make a difference.