For any struct or class, we may sometimes need to use the assignment operator, as in "A = B". There is a default assignment operator provided by C++, but it is not always adequate. We may therefore sometimes need to provide our own assignment operator.
Simple example of an assignment operator
Suppose we have the following class:
class Box { private: int Length; int Width; public: Box(); ... (lots of things)... };
We would like to have an assignment operator for Box. It would look something like this:
Prototype statement (put this in the class definition):
Box & operator =(const Box &);
Code:
Box & Box::operator =(const Box & Other) { Length = Other.Length; Width = Other.Width; return *this; }
Let's go over this carefully. We use this in the form
A = B;
and it is equivalent to:
A.operator =(B);
That is, the current instance is the one on the left.
The argument is a constant reference to a Box; notice we do not change the value of B when we write "A = B". It is a reference to avoid an unnecessary copy operation.
The actual work being done is just copying the data, in this case.
The return type is Box & and we eventually return *this. Here this is a pointer to the current instance (provided free of charge with every class). We return a reference, again to avoid an unnecessary copy operation.
Why should the assignment operator return anything at all? The reason is that people may want to write chained assignment statements like:
A = B = C = D;
This is executed from the right to the left:
If the assignment operator did not return anything at all, we could not have chained assignment statements, which are part of the overall C++ language.
What this example does is more or less exactly what the default assignment operator would do for use anyway.
Slightly more complicated example
Suppose our Box also has a name
class Box { private: int Length; int Width; char * Name; public: Box(); ... (lots of things)... Box & operator =(const Box &); };
Here the space for the Name is allocated dynamically. We will assume this is a C-style string.
Now the assignment operator will have more work to do. It needs to copy the Name as well.
Box & Box::operator =(const Box & Other) { int M, N; if (this != &Other) //Check for self-assignment { if (Name != 0) //Delete old memory delete [] Name; N = strlen(Other.Name); Name = new char[N+1]; //Allocate new memory for (M = 0; M < N; M++) //Copy the data Name[M] = Other.Name[M]; Name[N] = '/0'; Length = Other.Length; Width = Other.Width; } return *this; //return *this }
Notice we don't just set Name = Other.Name, which is what the default assignment operator would do. That could cause problems. Instead, we make a copy of the string involved.
Whenever a class has data members that are pointers, we need to worry about problems of this kind. The steps in writing our own assignment operator are the following:
Notice the worry about self-assignment, as in "A = A". We need to check for this both to avoid unnecessary work and to avoid problems later when we delete the old memory. If we didn't have pointers involved, this would not be a problem.
Other ways to write the assignment operator
Instead of making the assignment operator a method of the Box class, we could make it a friend of the class. In the class definition, we would have a prototype statement like this:
friend Box & bool operator =(Box &, const Box &);
and in the code file we would have
Box & operator =(Box & First, const Box & Second) { First.Length = Second.Length; First.Width = Second.Width; return First; }
As a friend function, the operator will have access to Length and Width (which are private data members).
If we had access methods for Box, such as (in the class definition):
int GetLength() const; int GetWidth() const; void Setlength(const int &); void SetWidth(const int &);
then we could define the assignment operator = as a stand-alone function. Its prototype would not have to be in the class definition and could be somewhere else:
Box & operator =(Box &, const Box &);
and the code for it would use the access functions:
bool operator ==(Box & First, const Box & Second) { First.SetLength(Second.GetLength()); First.SetWidth(Second.GetWidth()); return First; }