For instance, the + operator is overloaded for the string class so to mean concatenation (i.e. if str1 == "xxx" and str2 == "yyy" then str1 + str2 == "xxxyyy").
Essentially the operator is treated much like a method definition, but using the keyword 'operator' plus the specific symbol (e.g. operator+ for addition, operator- for subtraction, etc.).
The one oddity is that the first operand for the operation
is assumed to be the class object itself, e.g. if I had
a class named Data, and wanted to be able to add an integer
to a Data object (e.g. something like
Data D1; D2 = D1 + 3;)
then I would think of D1 as being the object 'calling'
the addition operator, 3 as the parameter, and the return
value would also be of type Data.
This would give an operator definition something like
class Data { public: Data operator(int v); ... whatever other stuff ... };
In the case of our NumericArray class, we might want to overload the + operator to allow us to add a value to every element in the numeric array, e.g.:
class NumericArray { // ... all the earlier stuff ... // overloaded assignment operator, allowing us to add // a numeric value to every element in an array // e.g. B + 3 would give a copy of B, but with // 3 added to each array element NumericArray operator+(double right); }; NumericArray NumericArray::operator+(double right) { if (arr != NULL) { for (int i = 0; i < size; i++) { arr[i] += right; } } return *this; } |
This is typically the desired behaviour, except when the class in question involves dynamically allocated memory.
Consider a linked list example, if the fields in our
linked list class were something like
node *front, *back;
(where node is the struct type for individual list nodes)
Then if we declare two lists, L1 and L2, and try L1 = L2;
then this would simply make L1's front and back point to L2's
front and back nodes - i.e. they would both be pointing at
the same list of nodes in memory.
Since the idea of assignment is more typically to make a copy of the right hand value, this falls short of the desired behaviour. (E.g. if we did a remove from L2 it would also remove the element from L1, since they're both pointing a the same collection of nodes!)
As a result, for classes that involve dynamic memory allocation it is often a good idea to write our own version of the assignment operator(s).
In the NumericArray example we were working with earlier, that might look like:
NumericArray NumericArray::operator=(const NumericArray& src) { if (src.arr == NULL) { arr = NULL; size = 0; return *this; } else { arr = new double[src.size]; if (arr == NULL) size = 0; else { size = src.size; for (int i = 0; i < size; i++) { arr[i] = src.arr[i]; } } return (*this); } } |