Chapter 12
Chapter 12
Operator Overloading
12-1
Operator Overloading
We have looked at ways to enhance the features of C++ by using function overloading
and overriding. We are going to look at a way to use some common C++ operators to
work with class objects.
We have been working with overloaded operators that are defined in the C++ libraries.
<<
- the stream-insertion operator
- bitwise left-shift operator
>>
- the stream-extraction operator
- the bitwise right-shift operator
*
- multiplication
- "is a pointer to"
- indirection operator (dereference)
+ and -
- these operators perform differently depending on whether they are operating on
integers, floating-point numbers, or pointers
The operators +, -, *, /, etc. are nothing more than functions that are called using a
different syntax for listing their arguments. The arguments are listed before and after the
operator rather than listed in parentheses after a function name. The job performed by
operator overloading could be performed by using explicit function calls, but operator
notation can sometimes be more clear.
value = a + b / c;
12-2
- Use operator overloading when it makes a program clearer than accomplishing
the same operations with explicit function calls.
To use a C++ operator on class objects, the operator MUST be overloaded - - with two
exceptions. The assignment operator (=) and the address operator (&).
- The assignment operator may be used with every class without overloading it. The
default behavior for the operator is memberwise assignment of the data members of
the class (recall the necessity for a copy constructor).
- Likewise, the address operator does not need to be overloaded and will simply
return the address of the object in memory.
Operators are overloaded by writing a function definition (heading and body) just as we
have done thus far. The name of the function becomes operator followed by the symbol
you wish to overload. Everything else remains virtually the same.
operator-
operator*
In the copy constructor example we had a special constructor that would ensure that
dynamically allocated memory was actually copied rather than just the address. This
takes care of statements such as
We are going to need to overload the assignment operator so the behavior of the
assignment expression will be as expected. The function heading for functions that
overload operators is as follows:
Just as with the copy constructor, a reference parameter is used so the compiler does not
make a copy and const so it cannot be changed.
12-3
The statement p2=p1; would cause p2's overloaded function to be called passing p1 as the
argument. The contents of p1 would be copied into p2. In other words, the object on the
left's member function is called with the object on the right as the argument.
class Person
{
public:
Person();
Person(char* nm, int age);
Person(const Person& obj)
~Person();
char* GetName()const;
int GetAge()const;
void SetData(char* nm, int age);
void operator=(const Person& obj);
private:
char* pName;
int pAge;
};
12-4
int main()
{
Person p1("Sam Jones",25);
cout << "\n\n" << p1.GetName() << ' ' << p1.GetAge();
Person p3;
cout << "\n\n" << p3.GetName() << ' ' << p3.GetAge();
Person p2 = p1;
cout << "\n\n" << p2.GetName() << ' ' << p2.GetAge();
p2.SetData("Sally Smith",45);
cout << "\n\n" << p1.GetName() << ' ' << p1.GetAge();
cout << "\n\n" << p2.GetName() << ' ' << p2.GetAge();
p3=p1;
cout << "\n\n" << p3.GetName() << ' ' << p3.GetAge();
cin.ignore(100,'\n');
cin.get();
return 0;
}
Basic Rules
- You cannot change the precedence of an operator by overloading it. If you wish to
change the order of evaluation you must use parentheses.
(a + b) + c = a + (b + c)
(a - b) - c != a - (b - c)
- You cannot change the number of operands required for a particular operator.
- unary operators must have one operand
- binary operators must have two operands
- the C++ ternary operator ( ?: ) cannot be overloaded
- You cannot change the way an operator works on built-in types. For example, you
cannot change the way the * operator multiplies two integer values.
- You cannot create new operators by using a combination of existing operators. For
example, you cannot copy the FORTRAN version of exponentiation by attempting to
define ** in your programs.
- To prevent changes to the way operators behave on built-in types, at least one
argument of an operator function must be a class object or a reference to a class object.
12-5
- Operator overloading must be done explicitly for each operator. Assume you have
overloaded the addition operator and the assignment operator to allow the following:
firstObj += secondObj;
When a member function is used to overload an operator, the leftmost operand must be
an object of the class or a reference to an object of the class. There are times when the
leftmost operand is an object of a different class or a built-in type. An example of this
would be the insertion and extraction (stream) operators. In such cases, the overloaded
function cannot be a member function. If the function requires access to private or
protected information, this function must be a friend of the class.
12-6
Multiple Assignment
is valid. The value of num3 is stored in num2 and returned. The return value is then stored in
num1.
12-7
Class Members or friend Functions?
class thisDemo
{
public:
void ShowVal();
private:
int val;
};
void thisDemo::ShowVal()
{
cout << val;
}
12-8
Consider a class that keeps track of an amount of money in dollars and cents.
class Money
{
public:
Money();
Money(int dlrs, int cnts);
void SetAmount(int dlrs, int cnts);
int GetDollars()const;
int GetCents()const;
void Adjust(); //adjusts amount when cents exceed 99 as well as
// handling negative values
Money operator+ (const Money& obj);
Money operator- (const Money& obj);
Money operator++();
Money operator++(int);
private:
int dollars;
int cents;
};
12-9
int main()
{
// Write a test program that adds two Money objects, subtracts two Money objects
// and adjusts the result as necessary. Given the following
// dollars cents dollars cents dollars cents
// 2 50 + 3 75 6 25
// 3 25 - 2 50 0 75
// 2 10 - 3 50 -1 -40
return 0;
}
Money::Money():dollars(0),cents(0) {}
Money::Money(int dlrs, int cnts):dollars(dlrs >= 0 ? dlrs : 0),
cents(cnts >= 0 ? cnts : 0) {}
void Money::SetAmount(int dlrs, int cnts)
{
dollars = (dlrs >= 0) ? dlrs : 0;
cents = (cnts >= 0 ? cnts : 0);
}
int Money::GetDollars()const
{
return dollars;
}
int Money::GetCents()const
{
return cents;
}
void Money::Adjust()
{
12-10
Money Money::operator- (const Money& obj)
{
12-11
Overloading Stream Operators (<< and >>)
class PhoneNum
{
public:
PhoneNum(string ac, string exc, string line);
void GetPhoneNum()const;
private:
string areaCode;
string exchange;
string line;
};
int main()
{
PhoneNum myPhone("949","582","4820");
string aCode, exch, line;
cout << "Enter your area code: ";
cin >> aCode;
cout << "Enter your exchange: " ;
cin >> exch;
cout << "Enter your line number: ";
cin >> line;
PhoneNum yourPhone(aCode,exch,line);
void PhoneNum::GetPhoneNum()const
{
cout << '(' << areaCode << ") " << exchange << '-' << line << "\n\n";
}
12-12
It would be extremely convenient to replace the several lines of code associated with
obtaining a phone number with the statement
Recall, if the operand on the left is an object of another class or a built-in data type, the
operator function must be implemented as a ___________________________ function.
To allow the function access to the private data without being forced to use the get and
set methods, it is a good idea to make this function a ___________________ of the class.
This function is not part of the class so although it may be declared anywhere in the class,
good style usually dictates that it be declared ___________________________________
_____________________________.
Let's take a look at a few of the details. The insertion and extraction operators have many
of the same properties of operators such as +, -, * etc. The insertion operator has a stream
variable as the left operand and a string literal, variable, or expression as the right
operand. Overloading this operator involves a little more thought than the arithmetic
operators. If we want to allow for expressions such as
12-13
class PhoneNum
{
public:
PhoneNum();
PhoneNum(string ac, string exc, string line);
void GetPhoneNum()const;
private:
string areaCode;
string exchange;
string line;
};
int main()
{
PhoneNum myPhone("949","582","4820");
PhoneNum yourPhone;
cout << "Enter your phone number in the form (xxx) xxx-xxxx: ";
cin >> yourPhone;
PhoneNum::PhoneNum() { }
12-14
istream& operator >>(istream& input, PhoneNum& num)
{
void PhoneNum::GetPhoneNum()const
{
cout << '(' << areaCode << ") " << exchange << '-' << line << "\n\n";
}
12-15