Discussion:
copy constructor for a class with arrays
(too old to reply)
Vladimir
2008-06-04 03:58:36 UTC
Permalink
Hi,
consider a class with a data-member of array type :
class A {
float X[5];
// other fields
}
Is it definitely true, that the default copy constructor will copy X element
by element, as if there were
for (int i=0; i<5;i++) { X[i] = other.X[i] } ?
Thank you.
Jason Cipriani
2008-06-04 05:09:36 UTC
Permalink
Post by Vladimir
Hi,
class A {
float X[5];
// other fields
}
Is it definitely true, that the default copy constructor will copy X
element by element, as if there were
for (int i=0; i<5;i++) { X[i] = other.X[i] } ?
Yes, see section 12.8/8:

"The implicitly-defined copy constructor for class X performs a memberwise
copy of its subobjects. The
order of copying is the same as the order of initialization of bases and
members in a user-defined constructor
(see 12.6.2). Each subobject is copied in the manner appropriate to its
type:
"...
"- if the subobject is an array, each element is copied, in the manner
appropriate to the element type;"

Jason
Jason Cipriani
2008-06-04 05:12:27 UTC
Permalink
Post by Jason Cipriani
Post by Vladimir
Hi,
class A {
float X[5];
// other fields
}
Is it definitely true, that the default copy constructor will copy X
element by element, as if there were
for (int i=0; i<5;i++) { X[i] = other.X[i] } ?
"The implicitly-defined copy constructor for class X performs a memberwise
copy of its subobjects. The
order of copying is the same as the order of initialization of bases and
members in a user-defined constructor
(see 12.6.2). Each subobject is copied in the manner appropriate to its
"...
"- if the subobject is an array, each element is copied, in the manner
appropriate to the element type;"
Oh just to make it clear, for the type in your example (float), it is like
X[i] =other.X[i]. For class types, though, copy constructors are used on the
individual elements, not assignment operators. I should've pasted the other
two cases for clarity:

- if the subobject is of class type, the copy constructor for the class is
used;
- if the subobject is an array, each element is copied, in the manner
appropriate to the element type;
- if the subobject is of scalar type, the built-in assignment operator is
used.
Alan Bellingham
2008-06-04 08:23:16 UTC
Permalink
Post by Vladimir
Hi,
class A {
float X[5];
// other fields
}
Is it definitely true, that the default copy constructor will copy X element
by element, as if there were
for (int i=0; i<5;i++) { X[i] = other.X[i] } ?
Yes. And no.

Yes, in that each member *will* be copied, as Jason says.

No, in that the compiler is permitted to do optimisation, and it may (at
its discretion) just do a memcpy if (and only if) the result of the copy
is indistinguishable in all ways from having done the looped copy.

Since there is no way for you to write legal code that can actually tell
from inside the program which occurred, you should assume the memberwise
copy actually took place. It's just that the generated code may not
actually have the loop in.

Alan Bellingham
--
Team Browns
<url:http://www.borland.com/newsgroups/> Borland newsgroup descriptions
<url:http://www.borland.com/newsgroups/netiquette.html> netiquette
Vladimir
2008-06-05 22:53:59 UTC
Permalink
... you should assume the memberwise
copy actually took place. It's just that the generated code may not
actually have the loop in.
Thank you, that is what I wanted to make sure of.
Actually I always took it as evident, but I was confused by the following
argument:
What is the "memberwise" copying in this case? The "member" of a class is an
array, not it's elements. If we assume that the memberwise copying is
essentially equivalent to A.Field = Source.Field given to every data-member,
it's not clear, how it works for a Field, which is an array.
I understood you so that the compiler will do the right job and copy
elements from one array to another (optimizing the job by memcpy, if
appropriate). So, it will work safe also if we change float to some type
with a non-trivial copy constructor, isn't it?
Thank you,
Vladimir
Alan Bellingham
2008-06-06 08:59:05 UTC
Permalink
Post by Vladimir
What is the "memberwise" copying in this case? The "member" of a class is an
array, not it's elements.
memberwise copying is recursive. So, if a member is another class, then
that class is copied memberwise.

Your question then comes down to: how is an *array* copied when it is a
subobject?

The answer is, element by element. ("if the subobject is an array, each
element is copied, in the manner appropriate to the element type."
12.8/8)

Alan Bellingham
--
Team Browns
ACCU Conference 2009: to be announced
Vladimir Grigoriev
2008-06-06 10:35:54 UTC
Permalink
Post by Vladimir
Thank you, that is what I wanted to make sure of.
Actually I always took it as evident, but I was confused by the following
What is the "memberwise" copying in this case? The "member" of a class is
an array, not it's elements. If we assume that the memberwise copying is
essentially equivalent to A.Field = Source.Field given to every
data-member, it's not clear, how it works for a Field, which is an array.
I understood you so that the compiler will do the right job and copy
elements from one array to another (optimizing the job by memcpy, if
appropriate). So, it will work safe also if we change float to some type
with a non-trivial copy constructor, isn't it?
Thank you,
Vladimir
You considered an array of built-in types. In this case a C++ compiler may
use such function as memcpy() for copying arrays as whole. However an array
may consist from elements each of them is an object in turn. In this case
for each element of the array a copy constructor is called.

Vladimir Grigoriev
Jason Cipriani
2008-06-06 18:34:18 UTC
Permalink
Post by Vladimir
... you should assume the memberwise
copy actually took place. It's just that the generated code may not
actually have the loop in.
Thank you, that is what I wanted to make sure of.
Note the operative word "may", though. You can't be guaranteed that the
compiler will generate any specific code given some specific C++ as input.
From your point of view, each element of the array is copied as if it's copy
constructor was called. PERIOD. The copy constructors, if you have them
defined, will always be called, per-element, guaranteed. Whether or not the
underlying copy is implemented with a loop or something else, is outside the
scope of C++.

And actually to be specific, it's *not* exactly like this:

for (int i=0; i<5;i++) { X[i] = other.X[i] }

That uses the assignment operator, not the copy constructor.

Jason

Ed Mulroy [TeamB]
2008-06-06 13:38:27 UTC
Permalink
I don't have the BDS 2007 drive mounted right now, so this answer was
confirmed with BCB 6.

When you declare an instance of class A
A source;
then it will not call the copy constructor for elements in source.X. It
will call the constructor with no or defaulted argument or if none is
supplied, the default constructor for each element in source.X.

If you then do an assignment such as:

A source;
A target;
:
target = source;

The constructor for each element will have been called for each element in
target.X at the point of declaration for target.

When the assignment is executed an assignment operator is called for each
element of target as in {pseudocode}
target.X[n].operator = (source.X[n]);

Run this to see what I mean:

-----------------------------------------------------------
#include <cstdio>

// helper function to handle unprintable chars
const char *CharOut(char c);


class ArrayClass
{
public:
ArrayClass(char cx = tracking_symbol) : c(cx)
{
ShowInit("Constructor", c);
}

ArrayClass(const ArrayClass & x) : c(x.c)
{
ShowInit("Copy Constructor", c);
}

ArrayClass& operator = (const ArrayClass & x)
{
c = x.c;
ShowInit("Assignment Operator", c);
return *this;
}

private:
char c;
static char tracking_symbol;

void ShowInit(const char *why, char x)
{
std::printf(
"%s \'%c\' \'%s\'\n",
why, tracking_symbol, CharOut(x)
);
++tracking_symbol;
}
};


char ArrayClass::tracking_symbol = 'A';


struct ContainerStruct
{
int num_items;
ArrayClass array[5];
};

ArrayClass item1(1);
ArrayClass item2(2);
ArrayClass item3(3);
ArrayClass item4(4);
ArrayClass item5(5);

int main()
{
ContainerStruct source;

std::printf("\nInitializing\n");
source.num_items = 5;
source.array[0] = item1;
source.array[1] = item2;
source.array[2] = item3;
source.array[3] = item4;
source.array[4] = item5;

ContainerStruct target;

std::printf("\nCopying\n");
target = source;
return 0;
}



const char *CharOut(char c)
{
static char result_str[8];
const char *format;

format = ((c >= '!') && (c <= '~')) ? "%c" : "\\x%02X";
std::sprintf(result_str, format, c);
return result_str;
}
-----------------------------------------------------------

. Ed
Post by Vladimir
class A {
float X[5];
// other fields
}
Is it definitely true, that the default copy constructor will copy X
element by element, as if there were
for (int i=0; i<5;i++) { X[i] = other.X[i] } ?
Loading...