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.
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.
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.
What is the difference between overloading the operator = in a class and the copy constructor?
In which context is each one called?
I mean, if I have the following:
Person *p1 = new Person("Oscar", "Mederos");
Person *p2 = p1;
Which one is used? And then when the other one is used?
Edit:
Just to clarify a little bit:
I already know that if we explicitly call the copy constructor Person p1(p2), the copy constructor will be used. What I wanted to know is when each one is used, but using the = operator instead, as #Martin pointed.
In your case neither is used as you are copying a pointer.
Person p1("Oscar", "Mdderos");
Person extra;
The copy constructor
Person P2(p1); // A copy is made using the copy constructor
Person P3 = p2; // Another form of copy construction.
// P3 is being-initialized and it uses copy construction here
// NOT the assignment operator
An assignment:
extra = P2; // An already existing object (extra)
// is assigned to.
It is worth mentioning that that the assignment operator can be written in terms of the copy constructor using the Copy and Swap idium:
class Person
{
Person(std::string const& f, std::string const& s);
Person(Person const& copy);
// Note: Do not use reference here.
// Thus getting you an implicit copy (using the copy constructor)
// and thus you just need to the swap
Person& operator=(Person copy)
{
copy.swap(*this);
return *this;
}
void swap(Person& other) throws()
{
// Swap members of other and *this;
}
};
The copy constructor is a constructor, it creates an object. In particular, the copy constructor creates an object which is semantically identical to another, already existing object, of which it makes a "copy":
Person newperson(oldperson); // newperson is a "copy" of oldperson
The assignment operator isn't a constructor at all, but an ordinary member funcion that can only be invoked on an existing object. Its purpose is to assign to your object the semantics of another object, so that after the assignment the two are semantically identical. You are not usually "overloading" the assignment operator, you are just defining it.
Person p; // construct new person
/* dum-dee-doo */
p = otherperson; // assign to p the meaning of otherperson, overwriting everything it was before
// invokes p.operator=(otherperson)
Note that if it makes sense to compare to objects (with ==), then both copy construction and assignment should behave so that we have equality afterwards:
Person p1(p2);
assert(p1 == p2);
p1 = p3;
assert(p1 == p3);
You are not forced to guarantee this, but users of your class will usually assume this behaviour. In fact, the compiler assumes that Person p1; Person p2(p1); entails p1 == p2;.
Lastly, as a final aside and as said elsewhere, note that Person p = p2; literally means Person p(p2) (copy construction), and never Person p; p = p2;. That's syntactic sugar to allow you to write naturally-looking code without compromising efficiency (or even correctness, as your class may not even be default-constructible).
A copy constructor constructs a new object by using the content of the argument object. An
overloaded assignment operator assigns the contents of an existing object to another existing
object of the same class.
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.