I have a class which does not have copy constructor or operator= overloaded.
The code is pretty big but the issue is around this pseudo-code:
ClassA object1(x,y);
object1.add(z)
myVector.push_back(object1);
//Now when I retrieve from myVector and do add it
// apparently creates another object
myVector.at(index).add(z1);
Like I said it is pseudo-code. I hope it make sense to experts out there!
So, ClassA looks like this (of course not all data members included)
Class ClassA {
private:
int x;
string y;
ClassB b;
vector<int> i;
public:
int z;
}
Since ClassB b is a new data member for this release, is the need of copy constructor now become a must?
Thanks again all of you for responding.
Class ClassB {
private:
vector<ClassC*> c;
Class D
}
Class ClassC {
private:
vector<ClassE*> e;
}
Class ClassD{
private:
vector<ClassF*> f;
}
Then ClassE and ClassF have basic types like int and string.
The new object isn't being created when you retrieve the object using
at(); at() returns a reference to it. The new object is being
created when you do the push_back(). And if you don't have an
accessible copy constructor or assignment operator, you can't put the
object into a vector; officially, it's undefined behavior, but at least
if you use the vector (as you've done here), it will in fact not
compile. Most likely you're getting the compiler generated defaults.
Without seeing the actual object type, it's impossible for us to say
whether they're appropriate; if the object type only contains basic
types and types from the standard library (other than the iostream stuff
and auto_ptr—and the threading stuff if you're using C++11),
then the compiler generated copy constructor and assignment should be
OK. If the class contains pointers to memory you allocate in the
constructor, it almost certainly isn't.
It must create a temporary when retrieving because the value stored is a copy.
I have a class which does not have copy constructor or operator= overloaded.
This does not really matter. Unless you explicitly disable them (declare as private and don't implement in C++03, define as delete in C++11) the compiler will generate one for you.
Now, if you do disable copying of your objects you will realize that you can no longer store them directly in a vector, as the vector will copy the argument internally. You could on the other hand store a (smart) pointer in the container, but this might actually complicate the problem you want solved.
I doubt that it is using a copy constructor, but just plain old struct copy.
myVector.at(index)
returns ClassA in this case, not ClassA &
You should be able to solve this by:
ClassA & refObj = myVector.at(index);
refObj.add(z1);
I put together a little test which had surprising results on Visual Studio.
class Boo {
public:
void add() { a++; }
int a;
};
vector<Boo> v;
Boo b;
b.a = 1;
for (int i=0; i<5; i++) {
v.push_back(b);
}
v[0].add();
v.at(1).add();
Boo & refB = v[2]; refB.add();
refB = v.at(3); refB.add();
Boo & refB2 = v.at(4); refB2.add();
printf("%d %d %d %d %d\n", v[0].a, v[1].a, v[2].a, v[3].a, v[4].a);
Results:
2 2 2 1 2
So the compiler treats:
Boo & refB = v[2]; refB.add();
refB = v.at(3); refB.add();
Differently than:
Boo & refB2 = v.at(4); refB2.add();
But I don't get the original problem (that is, v.at(n).add() is not copying, but passing by reference). Are you sure the following line is copying?
myVector.at(index).add(z1)
Could this be compiler specific? What compiler / OS are you using?
I have a class which does not have copy constructor or operator= overloaded.
In that case, the compiler provides these for you. Quoting the C++ 2003 standard, clause 12.8:
If the class definition does not explicitly declare a copy constructor, one is declared implicitly. … The implicitly-declared copy constructor for a class X will have the form
X::X(const X&) [or] X::X(X&). … The implicitly-defined copy constructor for class X performs a memberwise copy of its subobjects.
and
If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly. … The implicitly-declared copy assignment operator for a class X will have the form X& X::operator=(const X&) [or] X& X::operator=(X&). … The implicitly-defined copy assignment operator for class X performs memberwise assignment of its subobjects.
The push_back creates a copy as it receives the parameter by value. If you dont want to use a copy constructor define the vector as a vector of pointers to your object, and push a pointer instead.
Related
Let's look at a simple example:
struct some_struct {
std::string str;
int a, b, c;
}
some_struct abc, abc_copy;
abc.str = "some text";
abc.a = 1;
abc.b = 2;
abc.c = 3;
abc_copy = abc;
Then abc_copy is an exact copy of abc.. how is it possible without defining the = operator?
(This took me by surprise when working on some code..)
If you do not define these four methods (six in C++11) the compiler will generate them for you:
Default Constructor
Copy Constructor
Assignment Operator
Destructor
Move Constructor (C++11)
Move Assignment (C++11)
If you want to know why?
It is to maintain backward compatibility with C (because C structs are copyable using = and in declaration). But it also makes writing simple classes easier. Some would argue that it adds problems because of the "shallow copy problem". My argument against that is that you should not have a class with owned RAW pointers in it. By using the appropriate smart pointers that problem goes away.
Default Constructor (If no other constructors are defined)
The compiler generated default constructor will call the base classes default constructor and then each members default constructor (in the order they are declared)
Destructor (If no destructor defined)
Calls the destructor of each member in reverse order of declaration. Then calls the destructor of the base class.
Copy Constructor (If no copy constructor is defined)
Calls the base class copy constructor passing the src object. Then calls the copy constructor of each member using the src objects members as the value to be copied.
Assignment Operator
Calls the base class assignment operator passing the src object. Then calls the assignment operator on each member using the src object as the value to be copied.
Move Constructor (If no move constructor is defined)
Calls the base class move constructor passing the src object. Then calls the move constructor of each member using the src objects members as the value to be moved.
Move Assignment Operator
Calls the base class move assignment operator passing the src object. Then calls the move assignment operator on each member using the src object as the value to be copied.
If you define a class like this:
struct some_struct: public some_base
{
std::string str1;
int a;
float b;
char* c;
std::string str2;
};
What the compiler will build is:
struct some_struct: public some_base
{
std::string str1;
int a;
float b;
char* c;
std::string str2;
// Conceptually two different versions of the default constructor are built
// One is for value-initialization the other for zero-initialization
// The one used depends on how the object is declared.
// some_struct* a = new some_struct; // value-initialized
// some_struct* b = new some_struct(); // zero-initialized
// some_struct c; // value-initialized
// some_struct d = some_struct(); // zero-initialized
// Note: Just because there are conceptually two constructors does not mean
// there are actually two built.
// value-initialize version
some_struct()
: some_base() // value-initialize base (if compiler generated)
, str1() // has a normal constructor so just call it
// PODS not initialized
, str2()
{}
// zero-initialize version
some_struct()
: some_base() // zero-initialize base (if compiler generated)
, str1() // has a normal constructor so just call it.
, a(0)
, b(0)
, c(0) // 0 is NULL
, str2()
// Initialize all padding to zero
{}
some_struct(some_struct const& copy)
: some_base(copy)
, str1(copy.str1)
, a(copy.a)
, b(copy.b)
, c(copy.c)
, str2(copy.str2)
{}
some_struct& operator=(some_struct const& copy)
{
some_base::operator=(copy);
str1 = copy.str1;
a = copy.a;
b = copy.b;
c = copy.c;
str2 = copy.str2;
return *this;
}
~some_struct()
{}
// Note the below is pseudo code
// Also note member destruction happens after user code.
// In the compiler generated version the user code is empty
: ~str2()
// PODs don't have destructor
, ~str1()
, ~some_base();
// End of destructor here.
// In C++11 we also have Move constructor and move assignment.
some_struct(some_struct&& copy)
// ^^^^ Notice the double &&
: some_base(std::move(copy))
, str1(std::move(copy.str1))
, a(std::move(copy.a))
, b(std::move(copy.b))
, c(std::move(copy.c))
, str2(std::move(copy.str2))
{}
some_struct& operator=(some_struct&& copy)
// ^^^^ Notice the double &&
{
some_base::operator=(std::move(copy));
str1 = std::move(copy.str1);
a = std::move(copy.a);
b = std::move(copy.b);
c = std::move(copy.c);
str2 = std::move(copy.str2);
return *this;
}
};
In C++, structs are equivalent to classes where members default to public rather than private access.
C++ compilers will also generate the following special members of a class automatically if they are not provided:
Default constructor - no arguments, default initalizes everything.
Copy constructor - ie a method with the same name as the class, that takes a reference to another object of the same class. Copies all values across.
Destructor - Called when the object is destroyed. By default does nothing.
Assignment operator - Called when one struct/class is assigned to another. This is the automatically generated method that's being called in the above case.
That behavior is necessary in order to maintain source compatibility with C.
C does not give you the ability to define/override operators, so structs are normally copied with the = operator.
But it is defined. In the standard. If you supply no operator =, one is supplied to you. And the default operator just copies each of the member variables. And how does it know which way to copy each member? it calls their operator = (which, if not defined, is supplied by default...).
The assignment operator (operator=) is one of the implicitly generated functions for a struct or class in C++.
Here is a reference describing the 4 implicitly generated members:
http://www.cs.ucf.edu/~leavens/larchc++manual/lcpp_136.html
In short, the implicitly generated member performs a memberwise shallow copy. Here is the long version from the linked page:
The implicitly-generated assignment operator specification, when needed, is the following. The specification says that the result is the object being assigned (self), and that the value of the abstract value of self in the post-state self" is the same as the value of the abstract value of the argument from.
// #(#)$Id: default_assignment_op.lh,v 1.3 1998/08/27 22:42:13 leavens Exp $
#include "default_interfaces.lh"
T& T::operator = (const T& from) throw();
//# behavior {
//# requires assigned(from, any) /\ assigned(from\any, any);
//# modifies self;
//# ensures result = self /\ self" = from\any\any;
//# ensures redundantly assigned(self, post) /\ assigned(self', post);
// thus
//# ensures redundantly assigned(result, post) /\ assigned(result', post);
//# }
The compiler will synthesise some members for you if you don't define them explicitly yourself. The assignment operator is one of them. A copy constructor is another, and you get a destructor too. You also get a default constructor if you don't provide any constructors of your own. Beyond that I'm not sure what else but I believe there may be others (the link in the answer given by 280Z28 suggests otherwise though and I can't remember where I read it now so maybe it's only four).
structs are basically a concatenation of its components in memory (with some possible padding built in for alignment). When you assign one struct the value of another, the values are just coped over.
I am new to object oriented programming, and this may be a silly question, but I don't understand why is using class A code better to use than class B if you want to create copy of one object.
class A {
int num;
public:
A(const A &ref) : num(ref.num) {};
};
class B {
int num;
public:
B(B *ptToClass) : num(ptToClass->num) {};
};
If I got this right, copy constructor is used in class A.
If you don't declare a copy constructor for your class, the compiler will declare one for you anyway. Classes have to have copy constructors. They're baked into the language and have special status. The language doesn't work without them.
Possibly the best example is that copy constructors are needed when passing by value. The compiler is going to implicitly call the copy constructor when you pass by value. It's not going to call the constructor B::B(B*).
So if you want your class to be copyable, you should define the copying logic in the copy constructor. It's just easier.
Class A is flexible and safe: you create a copy from any A object you have, even if it's a temporary one.
Class B is less safe as you could invoke the constructor with a nullptr. It's less flexible because you can only use ypur constructor to copy an object from which you can get the address and which is not const.
B b1(...);
const B b2(...);
B fb(); // function returning a B
B b3(&b1);
B b4(&b2); // error b2 is const
B b5(&fb()); // error you can't take adress of a temporary
The thing is that if a constructor is considered to be a copy constructor by the compiler, it is used in special ways. For instance, if you have a function that takes a parameter of your type A by copy, like this:
void function(A obj) {
// Do something with A
// ...
}
And then you call that function:
int main() {
A a_obj;
function(a_obj);
}
the object obj received by function will be created by the copy constructor you provided. So, it is a nice thing to provide copy constructor for your classes that are meant to be copied, so that them fits more nicely with the languages features and libraries.
There is no problem in creating a constructor of the kind in your class B, if that fit your needs in your application, but that will not be understood by the compiler as a copy constructor, and won't be used when the compiler or libraries needs to copy your objects.
It is forbidden by standard to use pointers in copy constructors:
C++ draft standard n3376 - section 12.8.2:
A non-template constructor for class X is a copy constructor if its
first parameter is of type X&, const X&, volatile X& or const volatile
X&, and either there are no other parameters or else all other
parameters have default arguments
Why is the argument of the copy constructor a reference rather than a pointer?
I think a more appropriate question to ask is: when to provide a user-defined copy constructor over the default one provided by the compiler?
As we know, the compiler provides a copy constructor (the default one) which does a memberwise copy.
This memberwise copy is not bad as long as the class is a simple concrete type (that behaves for the most part like a built-in type). But for complex concrete types or classes with hierarchies, memberwise copy is not a good idea, and the programmer is highly advised to provide his own implementation of the copy constructor (that is, provide user-defined copy constructor).
As a thumb rule, it is a good idea to provide user-defined copy constructor in the class.
In trying to understand this answer, it seems that new can be classified as a "copy constructor" and delete sometimes as a "trivial destructor".
I can find next to nothing (that I can quickly grasp) on "trivial assignment" except that it is "trivial if it is implicitly declared, if its class has no virtual member functions or virtual base classes, and if its direct base classes and embedded objects have a trivial assignment operator".
I found a question on yahoo about implicit declaration, but to my surprise, it did not answer.
My brain hurts after reading about virtual member functions.
Besides, I'm a monkey-see-monkey-do programmer, so the only way I'm going to get this is to see it in action. Please explain with respect to the above definition of "trivial" by using the example provided in the first answer, that std::string has a trivial assignment operator by using new or delete or for a less apparent reason.
New can use a copy constructor, and delete uses a destructor. The copy constructor or destructor may be trivial.
That being said, there is a STRONG chance that you will not need to worry about whether a constructor/destructor is trivial for a long time.
new calls a constructor to construct the object. If the one argument to the constructor of type T is an instance of T that is a copy constructor: you are trying to construct an instance of one object from another
class Foo
{
public:
Foo(int x) // not a copy constructor
: mX(x)
{ }
Foo(const Foo& inOther) // copy constructor
: mX(inOther.mX)
{ }
private:
int mX;
};
class Bar
{
public:
Bar(int x)
: mX(x)
{ }
// no copy constructor specified.. C++ will build an implicit one for you
private:
int mX;
}
};
Foo a(1); // uses the first constructor (not a copy)
Foo b(a); // uses a copy constructor
Foo c = a; // copy constructor
Foo* d = new Foo(1); // construct a new instance of Foo (not a copy)
Foo* e = new Foo(a); // copy
Bar f(1); // normal constructor
Bar g(f); // IMPLICIT copy constructor
If your class does not have a copy constructor, like Bar, C++ usually provides you one (always provides you one unless you have an explicit constructor or delete the copy constructor with a C++11 keyword). This copy constructor is very straightforward: it copies each member of your class.
A trivial copy constructor is special. A trivial copy constructor can only be created when the copy constructor is implicitly created for you by the compiler and:
All members of your class are trivially copyable
You do not have any virtual methods or virtual base classes
All of your base classes are trivially copyable.
If you specify a constructor in your class, it is not trivial, by definition. Foo does not have a trivial copy constructor because it is user defined. Bar has an implicit copy constructor because it is not user defined. The implicit copy constructor IS trivial, because copying mX is trivial (copying ints is trivial).
Similar rules go for destructors. A trivial destructor follows the same rules, and delete
WHat does it do for you? The spec lists a few key behaviors about trivial constructors/destructors. In particular, there's a list of things you can do if you have a trivial constructor and destructor that are illegal otherwise. However, they are all very nuanced, and unimportant to 99.9% of C++ code development. They all deal with situations where you can get away with not constructing or destructing an object.
For example, if I have a union:
union MyUnion {
int x;
ClassA y;
ClassB z;
}
If y and z have trivial constructors and destructors, C+ will write a copy constructor for that union for me. If one of them has a non-trivial constructor/destructor, I have to write the copy constructor for the union myself.
Another thing you can do is fast destruction of an array. Usually, when you delete an array, you have to make sure to call the destructor on every item. If you can prove that the destructor of each element is trivial, then you are allowed to skip destroying the elements, and just free the memory. std::vector does this under the hood (so you don't have to)
Could somebody explain me the difference between copying and assignment?
SomeClass a;
SomeClass b = a; // assignment
SomeClass c(a); // assignment
b = c; // copying
but what is the difference, why there are two different constructions in the language?
Initialization only happens once, when the object is created. If by copying you mean calling the copy constructor, than copying is a form of initialization. Assignment can happen any number of times.
Now, on to your example, all of those are wrong:
SomeClass a();
This declares a method called a which takes no parameters and returns an object SomeClass.
SomeClass b = a; // actually copy constructor & initialization of b
SomeClass c(a); // same
If a were a SomeClass object, these two would be initialization, and it calls the copy constructor - SomeClass::SomeClass(const SomeClass&). They are equivalent.
b = c; // assignment
If c is a SomeClass object, this is assignment. It callse SomeClass::operator =(const SomeClass&).
This is initialization (but it calls the copy constructor):
SomeClass b = a;
So is this:
SomeClass c(a);
This is assignment:
b = c;
Oh, and this isn't an initialization:
SomeClass a();
Copying is for initializing new objects by copying the contents of existing ones, assignment is for overwriting existing objects with the contents of other objects - the two are very different things. In particular, this
SomeClass a;
SomeClass b = a;
is copy initialization - you're copying a to create a new SomeClass called b using syntax of the form
T x = y;
This has the effect of invoking SomeClass's copy constructor (assuming there is one and it's accessible). The compiler-generated default copy constructor would do a memberwise copy of a; you can replace it with your own as needed, e.g.
SomeClass(const SomeClass& rhs)
: x(rhs.x)
{}
(Note that this is a very boring example, as it just does what the default memberwise copy constructor might.)
Moving on, this
SomeClass c(a);
is direct initialization using the copy constructor. It will generally have the same effect as the above, but this is worth a read:
http://www.gotw.ca/gotw/036.htm
Also, see here:
http://www.gotw.ca/gotw/001.htm
Your final case, namely
b = c;
is assignment. The semantics of this should generally be to overwrite b with the contents of c (although some things, such as std::auto_ptr, have strange assignment semantics, so watch out). To implement your own assignment operator, you write something like this (note that this is a very boring example, as it just does what the default memberwise assignment operator might):
SomeClass& operator=(const SomeClass& rhs)
{
x = rhs.x;
return *this;
}
In practice, however, you have to be careful about exception safety in situations like this, which leads to things like the popular copy-and-swap idiom for implementing assignment operators. See here:
http://en.wikibooks.org/wiki/More_C++_Idioms/Copy-and-swap
Initializations initialize a previously uninitialized object. Assignments, on the other hand, overwrite an already initialized object and may have to destroy existing state. These are different operations; while they usually have the same outcome for the object on the LHS, they are not semantically equivalent.
Let's look at a simple example:
struct some_struct {
std::string str;
int a, b, c;
}
some_struct abc, abc_copy;
abc.str = "some text";
abc.a = 1;
abc.b = 2;
abc.c = 3;
abc_copy = abc;
Then abc_copy is an exact copy of abc.. how is it possible without defining the = operator?
(This took me by surprise when working on some code..)
If you do not define these four methods (six in C++11) the compiler will generate them for you:
Default Constructor
Copy Constructor
Assignment Operator
Destructor
Move Constructor (C++11)
Move Assignment (C++11)
If you want to know why?
It is to maintain backward compatibility with C (because C structs are copyable using = and in declaration). But it also makes writing simple classes easier. Some would argue that it adds problems because of the "shallow copy problem". My argument against that is that you should not have a class with owned RAW pointers in it. By using the appropriate smart pointers that problem goes away.
Default Constructor (If no other constructors are defined)
The compiler generated default constructor will call the base classes default constructor and then each members default constructor (in the order they are declared)
Destructor (If no destructor defined)
Calls the destructor of each member in reverse order of declaration. Then calls the destructor of the base class.
Copy Constructor (If no copy constructor is defined)
Calls the base class copy constructor passing the src object. Then calls the copy constructor of each member using the src objects members as the value to be copied.
Assignment Operator
Calls the base class assignment operator passing the src object. Then calls the assignment operator on each member using the src object as the value to be copied.
Move Constructor (If no move constructor is defined)
Calls the base class move constructor passing the src object. Then calls the move constructor of each member using the src objects members as the value to be moved.
Move Assignment Operator
Calls the base class move assignment operator passing the src object. Then calls the move assignment operator on each member using the src object as the value to be copied.
If you define a class like this:
struct some_struct: public some_base
{
std::string str1;
int a;
float b;
char* c;
std::string str2;
};
What the compiler will build is:
struct some_struct: public some_base
{
std::string str1;
int a;
float b;
char* c;
std::string str2;
// Conceptually two different versions of the default constructor are built
// One is for value-initialization the other for zero-initialization
// The one used depends on how the object is declared.
// some_struct* a = new some_struct; // value-initialized
// some_struct* b = new some_struct(); // zero-initialized
// some_struct c; // value-initialized
// some_struct d = some_struct(); // zero-initialized
// Note: Just because there are conceptually two constructors does not mean
// there are actually two built.
// value-initialize version
some_struct()
: some_base() // value-initialize base (if compiler generated)
, str1() // has a normal constructor so just call it
// PODS not initialized
, str2()
{}
// zero-initialize version
some_struct()
: some_base() // zero-initialize base (if compiler generated)
, str1() // has a normal constructor so just call it.
, a(0)
, b(0)
, c(0) // 0 is NULL
, str2()
// Initialize all padding to zero
{}
some_struct(some_struct const& copy)
: some_base(copy)
, str1(copy.str1)
, a(copy.a)
, b(copy.b)
, c(copy.c)
, str2(copy.str2)
{}
some_struct& operator=(some_struct const& copy)
{
some_base::operator=(copy);
str1 = copy.str1;
a = copy.a;
b = copy.b;
c = copy.c;
str2 = copy.str2;
return *this;
}
~some_struct()
{}
// Note the below is pseudo code
// Also note member destruction happens after user code.
// In the compiler generated version the user code is empty
: ~str2()
// PODs don't have destructor
, ~str1()
, ~some_base();
// End of destructor here.
// In C++11 we also have Move constructor and move assignment.
some_struct(some_struct&& copy)
// ^^^^ Notice the double &&
: some_base(std::move(copy))
, str1(std::move(copy.str1))
, a(std::move(copy.a))
, b(std::move(copy.b))
, c(std::move(copy.c))
, str2(std::move(copy.str2))
{}
some_struct& operator=(some_struct&& copy)
// ^^^^ Notice the double &&
{
some_base::operator=(std::move(copy));
str1 = std::move(copy.str1);
a = std::move(copy.a);
b = std::move(copy.b);
c = std::move(copy.c);
str2 = std::move(copy.str2);
return *this;
}
};
In C++, structs are equivalent to classes where members default to public rather than private access.
C++ compilers will also generate the following special members of a class automatically if they are not provided:
Default constructor - no arguments, default initalizes everything.
Copy constructor - ie a method with the same name as the class, that takes a reference to another object of the same class. Copies all values across.
Destructor - Called when the object is destroyed. By default does nothing.
Assignment operator - Called when one struct/class is assigned to another. This is the automatically generated method that's being called in the above case.
That behavior is necessary in order to maintain source compatibility with C.
C does not give you the ability to define/override operators, so structs are normally copied with the = operator.
But it is defined. In the standard. If you supply no operator =, one is supplied to you. And the default operator just copies each of the member variables. And how does it know which way to copy each member? it calls their operator = (which, if not defined, is supplied by default...).
The assignment operator (operator=) is one of the implicitly generated functions for a struct or class in C++.
Here is a reference describing the 4 implicitly generated members:
http://www.cs.ucf.edu/~leavens/larchc++manual/lcpp_136.html
In short, the implicitly generated member performs a memberwise shallow copy. Here is the long version from the linked page:
The implicitly-generated assignment operator specification, when needed, is the following. The specification says that the result is the object being assigned (self), and that the value of the abstract value of self in the post-state self" is the same as the value of the abstract value of the argument from.
// #(#)$Id: default_assignment_op.lh,v 1.3 1998/08/27 22:42:13 leavens Exp $
#include "default_interfaces.lh"
T& T::operator = (const T& from) throw();
//# behavior {
//# requires assigned(from, any) /\ assigned(from\any, any);
//# modifies self;
//# ensures result = self /\ self" = from\any\any;
//# ensures redundantly assigned(self, post) /\ assigned(self', post);
// thus
//# ensures redundantly assigned(result, post) /\ assigned(result', post);
//# }
The compiler will synthesise some members for you if you don't define them explicitly yourself. The assignment operator is one of them. A copy constructor is another, and you get a destructor too. You also get a default constructor if you don't provide any constructors of your own. Beyond that I'm not sure what else but I believe there may be others (the link in the answer given by 280Z28 suggests otherwise though and I can't remember where I read it now so maybe it's only four).
structs are basically a concatenation of its components in memory (with some possible padding built in for alignment). When you assign one struct the value of another, the values are just coped over.