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.
Related
This is something I have wondered for a long time. Take the following example:
struct matrix
{
float data[16];
};
I know what the default constructor and destructor do in this specific example (nothing), but what about the copy constructor and the copy assignment operator?
struct matrix
{
float data[16];
// automatically generated copy constructor
matrix(const matrix& that) : // What happens here?
{
// (or here?)
}
// automatically generated copy assignment operator
matrix& operator=(const matrix& that)
{
// What happens here?
return *this;
}
};
Does it involve std::copy or std::uninitialized_copy or memcpy or memmove or what?
This is what the standard says in 12.8 (Copying class objects). Copy construction:
Each subobject is copied in the manner appropriate to its type:
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.
Copy assignment:
Each subobject is assigned in the manner appropriate to its type:
if the subobject is of class type, the copy assignment operator for the class is used (as if by explicit qualification; that is, ignoring any possible virtual overriding functions in more derived classes);
if the subobject is an array, each element is assigned, in the manner appropriate to the element type;
if the subobject is of scalar type, the built-in assignment operator is used.
Both copies elements in the array (instead of doing nothing or copying pointer).
struct X
{
char data_[100];
};
int main ()
{
X orig, copy_assign;
orig.data_[10] = 'a';
copy_assign = orig;
X copy_constructor(orig);
printf("orginal10:%c, copy_assign10:%c, copy_constructor10:%c\n",orig.data_[10],copy_assign.data_[10],copy_constructor.data_[10]);
copy_assign.data_[10] = 'b';
printf("original10:%c, copy_assign10:%c, copy_constructor10:%c\n",orig.data_[10],copy_assign.data_[10],copy_constructor.data_[10]);
copy_constructor.data_[10] = 'c';
printf("original10:%c, copy_assign10:%c, copy_constructor10:%c\n",orig.data_[10],copy_assign.data_[10],copy_constructor.data_[10]);
return 0;
}
running results:
orginal10:a, copy_assign10:a, copy_constructor10:a
original10:a, copy_assign10:b, copy_constructor10:a
original10:a, copy_assign10:b, copy_constructor10:c
From the first line of the result, we can see that at least something was copied (it is either the elements in the array, or the array pointer was copied).
From the next two lines, we can see that changing copy assigned objects and copy constructed objects' array did not change the original array. Therefore we conclude that elements were copied instead of array pointer.
Hope this example is clear.
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.
I have a class, Number, which represents a double with two digits after the dot. Before I overloaded the = operator I ran some tests and got a little confused:
int main(int argc, char **argv) {
Number *w = new Number(1.111);
Number *q = new Number(3.444);
*w = *q;
std::cout << w->GetNumber() <<std::endl;
delete q;
std::cout << w->GetNumber() <<std::endl;
Number e(5.555);
Number t = e;
std::cout <<t.GetNumber() <<std::endl;
}
The output was
3.44
3.44
5.55
What is the default action of '=' on user defined objects when '=' is not overloaded?
I was under the impression that when writing:
*w = *q;
w points to q (like in Java). However, in my example I deleted q and w still had the correct values.
Anyway, I got same results when I made assignments without overloading = and with overloading =.
The default operation of the assignment operator is memberwise assignment:
class Foo
{
int i;
std::string s;
Bar b;
public:
Foo& operator=(const Foo& that)
{
i = that.i;
s = that.s;
b = that.b;
return *this;
}
};
If that's exactly what you need, you don't have to overload operator= manually.
If you have pointer members, only the pointers are copied, not the pointees, which can wreak havoc. Either use RAII types like std::vector in that case or disable copying by declaring the copy constructor and the assignment operator private or by inheriting from boost::noncopyable.
Note that Number t = e; does not call the assignment operator, because there isn't yet any object to assign to. Instead, the copy constructor is called, which does memberwise copy construction by default:
Foo(const Foo& that) : i(that.i), s(that.s), b(that.b)
{
}
Since you will probably ask: the thing after the colon is called an initializer list. It cannot be overstated that initialization and assignment are two very different concepts in C++.
It makes a copy of the object (deep copy) i.e. the q object's contents are copied into w. If you have not provided your own copy constructor - assignment operator pair then compiler will automatically generate one for you which copies each element of the source object to the elements of the destination object. Once this copy is created, it doesn't matter what you do with the original object.
*p = *q is equivalent to
(*p).operator=(*q);
The default assignment operator does the following: (I tried putting it in short but I can't beat the standard in terms of accuracy and conciseness, hence quoting it)
The implicitly-defined copy assignment operator for class X performs memberwise assignment of its subobjects. The direct base classes of X are assigned first, in the order of their declaration in the base-specifierlist, and then the immediate nonstatic data members of X are assigned, in the order in which they were declared in the class definition. Each subobject is assigned in the manner appropriate to its type:
— if the subobject is of class type, the copy assignment operator for the class is used (as if by explicit qualification; that is, ignoring any possible virtual overriding functions in more derived classes);
— if the subobject is an array, each element is assigned, in the manner appropriate to the element type;
— if the subobject is of scalar type, the built-in assignment operator is used.
As you can see, the left hand side object does not start referring to the right hand side object.
Default operator = perform memberwise copy of the object to the destination.
*w = *q assign the value of *q to *w;
w = q makes w point to the same object as q, but it leaks w.
I know that the compiler sometimes provides a default copy constructor if you don't implement yourself. I am confused about what exactly this constructor does. If I have a class that contains other objects, none of which have a declared copy constructor, what will the behavior be? For example, a class like this:
class Foo {
Bar bar;
};
class Bar {
int i;
Baz baz;
};
class Baz {
int j;
};
Now if I do this:
Foo f1;
Foo f2(f1);
What will the default copy constructor do? Will the compiler-generated copy constructor in Foo call the compiler-generated constructor in Bar to copy over bar, which will then call the compiler-generated copy constructor in Baz?
Foo f1;
Foo f2(f1);
Yes this will do what you expect it to:
The f2 copy constructor Foo::Foo(Foo const&) is called.
This copy constructs its base class and then each member (recursively)
If you define a class like this:
class X: public Y
{
private:
int m_a;
char* m_b;
Z m_c;
};
The following methods will be defined by your compiler.
Constructor (default) (2 versions)
Constructor (Copy)
Destructor (default)
Assignment operator
Constructor: Default:
There are actually two default constructors.
One is used for zero-initialization while the other is used for value-initialization. The used depends on whether you use () during initialization or not.
// Zero-Initialization compiler generated constructor
X::X()
:Y() // Calls the base constructor
// If this is compiler generated use
// the `Zero-Initialization version'
,m_a(0) // Default construction of basic PODS zeros them
,m_b(0) //
m_c() // Calls the default constructor of Z
// If this is compiler generated use
// the `Zero-Initialization version'
{
}
// Value-Initialization compiler generated constructor
X::X()
:Y() // Calls the base constructor
// If this is compiler generated use
// the `Value-Initialization version'
//,m_a() // Default construction of basic PODS does nothing
//,m_b() // The values are un-initialized.
m_c() // Calls the default constructor of Z
// If this is compiler generated use
// the `Value-Initialization version'
{
}
Notes: If the base class or any members do not have a valid visible default constructor then the default constructor can not be generated. This is not an error unless your code tries to use the default constructor (then only a compile time error).
Constructor (Copy)
X::X(X const& copy)
:Y(copy) // Calls the base copy constructor
,m_a(copy.m_a) // Calls each members copy constructor
,m_b(copy.m_b)
,m_c(copy.m_c)
{}
Notes: If the base class or any members do not have a valid visible copy constructor then the copy constructor can not be generated. This is not an error unless your code tries to use the copy constructor (then only a compile time error).
Assignment Operator
X& operator=(X const& copy)
{
Y::operator=(copy); // Calls the base assignment operator
m_a = copy.m_a; // Calls each members assignment operator
m_b = copy.m_b;
m_c = copy.m_c;
return *this;
}
Notes: If the base class or any members do not have a valid viable assignment operator then the assignment operator can not be generated. This is not an error unless your code tries to use the assignment operator (then only a compile time error).
Destructor
X::~X()
{
// First runs the destructor code
}
// This is psudo code.
// But the equiv of this code happens in every destructor
m_c.~Z(); // Calls the destructor for each member
// m_b // PODs and pointers destructors do nothing
// m_a
~Y(); // Call the base class destructor
If any constructor (including copy) is declared then the default constructor is not implemented by the compiler.
If the copy constructor is declared then the compiler will not generate one.
If the assignment operator is declared then the compiler will not generate one.
If a destructor is declared the compiler will not generate one.
Looking at your code the following copy constructors are generated:
Foo::Foo(Foo const& copy)
:bar(copy.bar)
{}
Bar::Bar(Bar const& copy)
:i(copy.i)
,baz(copy.baz)
{}
Baz::Baz(Baz const& copy)
:j(copy.j)
{}
The compiler provides a copy constructor unless you declare (note: not define) one yourself. The compiler-generated copy constructor simply calls the copy constructor of each member of the class (and of each base class).
The very same is true for the assignment operator and the destructor, BTW. It is different for the default constructor, though: That is provided by the compiler only if you do not declare any other constructor yourself.
Yes, the compiler-generated copy constructor performs a member-wise copy, in the order in which the members are declared in the containing class. If any of the member types do not themselves offer a copy constructor, the would-be copy constructor of the containing class cannot be generated. It may still be possible to write one manually, if you can decide on some appropriate means to initialize the value of the member that can't be copy-constructed -- perhaps by using one of its other constructors.
The C++ default copy constructor creates a shallow copy. A shallow copy will not create new copies of objects that your original object may reference; the old and new objects will simply contain distinct pointers to the same memory location.
The compiler will generate the needed constructors for you.
However, as soon as you define a copy-constructor yourself, the compiler gives up generating anything for that class and will give and error if you don't have the appropriate constructors defined.
Using your example:
class Baz {
Baz(const Baz& b) {}
int j;
};
class Bar {
int i;
Baz baz;
};
class Foo {
Bar bar;
};
Trying to default instantiate or copy-construct Foo will throw an error since Baz is not copy-constructable and the compiler can't generate the default and copy constroctor for Foo.
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.