Effective Programming Techniques – task 2
Operator overloading
Why was operator overloading introduced?
 For a human, the notation 2+3=5 is intuitive and convenient. Let's imagine we have a
class whose objects represent numbers (e.g., very large numbers that can have even thousands
of digits). The basic operations we will perform on objects of this class will be addition,
subtraction, division, multiplication, and the assignment of values. The declaration of this class
might look like this:
#define NUMBER_DEFAULT_LENGTH 10
class CNumber
{
public:
 CNumber() {i_length = NUMBER_DEFAULT_LENGTH; pi_number = new
int[i_length];};
 ~CNumber() {delete pi_number;}
void vSet(int iNewVal);
void vSet(CNumber &pcNewVal);
 CNumber vAdd(CNumber &pcNewVal);
 CNumber vSub(CNumber &pcNewVal);
 CNumber vMul(CNumber &pcNewVal);
 CNumber vDiv(CNumber &pcNewVal);
 CNumber vAdd(int iNewVal);
 CNumber vSub(int iNewVal);
 CNumber vMul(int iNewVal);
CNumber vDiv(int iNewVal);
private:
int *pi_number;
int i_length;
} //class CNumber
nstead of a pointer, a reference (symbol &) is used, which means the same thing but simplifies
the notation in this case. This will become clear in later examples.
Note that, according to the definition, the methods vAdd, vSub, vMul, vDiv do not modify the
object on which they were called, but instead return the result of the operation. This might seem
questionable from the perspective of object-oriented programming. When programming in an
object-oriented paradigm, we expect that a given method will modify the fields of the object on
which it is called. So, for example, one might say that an addition method should look like this:
„ZPR PWr – Zintegrowany Program Rozwoju Politechniki Wrocławskiej”
2/6
void vAdd(CNumber &pcNewVal);
and its usage like that:
CNumber c_value, c_add;
/* initialize c_value, c_add */
c_value.vAdd(c_add);
The result of the addition in the above example would be accumulated in the object c_value.
However, such an implementation of the arithmetic methods would be inconvenient; therefore,
according to the declaration of the CNumber class, the usage will look like this:
CNumber c_result, c_value, c_add;
/* initialize c_value, c_add */
c_result.vSet(c_value.cAdd(c_add));
The cAdd operation will not modify the c_value object but will return a result that is copied to
the c_result object.
If we want to perform the following calculations using the CNumber class:
result = (arg / 5)*2 + arg, where arg is the argument value entered by the user, it will look like
this::
CNumber c_result, c_arg;
/*initialize c_arg*/
c_result.vSet(c_arg.cDiv(5).cMul(2).cAdd(c_arg));
It could be argued that a significantly more readable notation would be:
CNumber c_result, c_arg;
/*initialize c_arg*/
c_result = (c_arg / 5) * 2 + c_arg;
Another advantage of the above notation is the ability to use parentheses and the order of
operations that is standard in mathematical notation. It can be argued that the more complex the
operation is, the more readable the notation using parentheses and operators should be
compared to the notation that calls successive methods.
„ZPR PWr – Zintegrowany Program Rozwoju Politechniki Wrocławskiej”
3/6
Operator overloading in C++
In C++, the following operators can be overloaded:
+ - * / % ^ & | ~ ! , < > <= >= ++ -- << >> == != && || += -= /= *= %= ^= &=
|= <<= >>= [] () -> ->* new new[] delete delete[]
Some of them are unary (e.g. operator ++), and most of them are binary (e.g. operator *).
The declaration of an operator has the form:
<type> operator <symbol> (<parameters>);
An example for the CNumber class:
class CNumber
{
public:
 CNumber() {i_length = NUMBER_DEFAULT_LENGTH; pi_number = new
int[i_length];};
 ~CNumber() {delete pi_number;}
void operator=(const CNumber &pcNewVal);
 CNumber operator+(CNumber &pcNewVal);
 CNumber operator*(CNumber &pcNewVal);
 CNumber operator-(CNumber &pcNewVal);
 CNumber operator/(CNumber &pcNewVal);
 CNumber operator+(int iNewVal);
 CNumber operator*(int iNewVal);
 CNumber operator-(int iNewVal);
 CNumber operator/(int iNewVal);
private:
int *pi_number;
int i_length;
}//class CNumber
Individual operators are implemented like regular methods, and they essentially are methods.
The operator notation always boils down to a series of calls to specific methods on the
respective objects. For example, the notation:
c_result = c_arg + 2;
is equivalent to calling
c_result.operator=(c_arg.operator+(2));
A particularly important operator is the assignment operator (operator=). Like the copy
constructor, it is implicitly defined for every class. As with the copy constructor, the default
operator=, works by copying the values of all the fields of the object. Consider when the
default operator= is sufficient and when it is necessary to define your own operator=.
Typically, when using operators, their arguments are taken as references, and the result is
returned statically. This allows for the following notation:
„ZPR PWr – Zintegrowany Program Rozwoju Politechniki Wrocławskiej”
4/6
c_result = c_arg + c_arg + 2;
If operators were to take pointers instead of references, it would look like this:
class CNumber
{
public:
//…
void operator=(const CNumber *pcNewVal);
 CNumber operator+(CNumber *pcNewVal);
 CNumber operator+(int iNewVal);
//…
} //class CNumber
Usage would be less convenient because, although a reference is a pointer, the code references
to variables that are references are the same as those for variables that are objects. In the case
of using pointers, the usage would look like this:
c_result = &(c_arg + &(c_arg + 2));
Note: When using operators and returning a result by value (as in the above examples), it
is often necessary to correctly define the copy constructor.
One of the drawbacks of using operators is the large number of object copies created. A detailed
analysis of which objects are created and when will be presented in the lecture. However,
it is possible and worthwhile to check this independently as part of the exercises in the lab.
Note: One of the solutions that can be applied when using operators is the so-called move
constructor. This topic will be addressed in 5th assignment, which introduces elements of
C++11 and C++14.
„ZPR PWr – Zintegrowany Program Rozwoju Politechniki Wrocławskiej”
5/6

Task
NOTES:
1. When writing your own program, you may use a different naming convention than
that presented in the task description and examples. However, you should adhere
to some consistent coding convention in accordance with the course requirements.
2. Exceptions are not allowed (this is just a reminder, as it directly follows from the
course rules).
3. You may only use syntax and features from the C++98 standard.
Program the CNumber class, which stores the digits of an integer in a dynamically allocated
array of int. Perform the following exercises:
1. Implement the operator=(const int iValue) that allows loading an int value into
an object of the CNumber class. Remember that a class that dynamically allocates an
array of variables must have a destructor that deletes this array.
2. Create two objects of the CNumber class and initialize them with int values. Then,
define the operator= (in the example, pi_table and i_length are, respectively, the
array storing the individual digits and the length of this array; remember that you can
use your own naming in your program) as given below and execute the following
program:
void CNumber::operator=(const CNumber &pcOther)
{
 pi_table = pcOther.pi_table;
 i_length = pcOther. i_length;
}//void CTable::operator=(CTable &pcOther)
CNumber c_num_0, c_num_1;
c_num_0 = 368;
c_num_1 = 1567;
c_num_0 = c_num_1;
 How did the execution of the above program end? Why?

3. Remove the destructor from the CNumber class. Did anything change? Why?
4. Write the code for the CNumber class so that the program given in excercise 2 executes
correctly and without memory leaks.
5. Write the method string sToStr(), hat returns the value of the CNumber object in
textual form. You can use this method to display the state of CNumber objects on the
screen.
„ZPR PWr – Zintegrowany Program Rozwoju Politechniki Wrocławskiej”
6/6
6. For the CNumber class, implement the operators (at least): +, -, *, and /. Each operator
should take another CNumber object as an argument. Remember that CNumber objects
can store very large numbers. Therefore, an implementation that converts CNumber
values to int, performs operations (such as multiplication) on int variables, and then
converts them back to CNumber is a poor solution and will be considered completely
incorrect.
Notes:
6.1. Extend the class to handle the storage of negative values.
6.2. Division should return the integer part of the result.
 provide solution for this task in c++