Operator Overloading
- Operators (
+, -, %, ==, ... ) are just functions - think of it as
+(x, 7) (+ is the function name, x, 7 are the arguments, returns sum of its arguments)
1. Via Non-member, non-friend of a class
1
2
3
| const Money operator +(const Money& amount1, const Money& amount2);
// const reference parameters for efficiency
// return type is Money
|
- Overloaded
+ above is NOT a member function of the class - Need to use accessor and mutator function since the overloaded function is not member function of the class
1
2
3
4
| const Money operator +(const Money& amount1, const Money& amount2) {
return Money(foo, bar);
}
// returns money class
|
- Constructor is not a void function. It return the class object itself but without given name.
- Called an ‘anonymous object’
Returning by const value
- Returning by const value : return value is also a class object, if we don’t put
const modifier, we can do things(calls the member function, etc…) unintentionally
1
2
3
| (m1+m2).output(); //Legal
(m1+m2).input(); // Legal but it changes the value of anonymous object
//Wierd
|
- To prevent those things to happen, we return by
const value - Then,
(m1+m2).input() will produce a compile error - Putting
const will do automatic error checking - Changing
(m1+m2) will cause a problem when the returned object is reference
Overloading Unary Operators
1
| const Money operatror -(const Money& amount);
|
- One argument (since 1 operand)
- opeartor can be overloaded twice : for binary, unary operator
2. Via member of a class
- When overloaded function is a ‘member operator’, calling object serves as the first parameter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| class Money{
public:
const Money operator +(const Money& amount2) const;
const Money operator -() const;
};
const Money Money::operator +(const Money& amount2) const {
int allCents1 = cents + dollors * 100;
//private member variables of the calling objects are accessed
int allCents2 = amount2.cents + amount2.dollars * 100;
//also private member variables of any object of the same class can be also accessed
...
}
const Money Money::operator -() const {
return Money(-dollars, -cents);
} //No argument!
|
Other overloads
- Function call opeartor
()- Must be overloaded as a member function
- Can overload for any number of arguments
1
2
3
4
5
6
7
| class Money {
public:
const Money operator ()(int a) const;
};
const Money Money::operator ()(int a) {
}
|
&&, || operator : recall short-circuit evaluation.anObject(42) can be thought of as anObject.()(42)
Automatic Type Conversion
- Even if we don’t overload operator with
int operand, it is legal if we provide constructors for automatic type conversion
1
2
3
4
5
6
7
| Money baseAmount(100, 60), fullAmount;
fullAmount = baseAmount + 25; // automatic type conversion happens
// fullAmount = 25 + baseAmount; // does not work
fullAmount.output();
Money::Money(int theDollars) : dollars(theDollars), cents(0) {}
// if provide constructors for automatic type conversion
|
- To make
25+baseAmount possible, must define as non-member function. (since member function regards first argument as the class object itself (calling obj).) Money+Money operator overloaded + Money(int) constructor overloaded = Money+int possible- But typically
int+Money is impossible for overloading via member function
3. Via non-member, friend of a class
- Overload operator as a nonmember
- Automatic type conversion of all arguments
- Causes inefficiency (Must use accessor or mutator, can’t access to private)
- Overload operator as a member
- Gives efficiency of bypassing accessor and mutator
- Asymmetry in automatic type conversion
Friend functions
- Friends can directly access private class data : no overhead, more efficient
- Use keyword
friend in front of the function declarationfriend keyword not used in function definition
- Can be located anywhere(
public, private, protected ) in the class definition - Since it is not member function, parameter list is similar as nonmember overloading operators
- Operator overloading is the most common usage of friend function but any function can be a friend of a class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| // 1. Non-member, 3. Friend
const Money operator +(const Money& amount1, const Money& amount2);
// 2. Member
class Money{
public:
const Money operator +(const Money& amount2) const;
// 3. Friend
friend const Money operator +(const Money&, const Money&);
// also inside the class definition
//but not considered as a member function
};
const Money Money::operator + (const Money& amount2) const {
}
|
Friend Classes
- Entire classes can be a friend of other class
- eg) class F is friend of class C
1
2
3
4
5
6
7
8
9
10
11
| class F; // forward declaration
class C
{
public :
friend class F;
};
class F
{
...
};
|
- All member functions of F are friends of C
- NOT reciprocated (single direction)
- Violates the pure spirit of OOP but for operators : very advantageous (automatic type conversion)
- Still encapsulates (because friend is in the class definition)
References
- Name of a storage location
- Similar to “pointer”
1
2
| int robert;
int& bob = robert;
|
bob is a reference to storage location for robert- Changes made to bob will affect robert
- Returning by a reference
- Allows some operator overload implementations (i.e.
<< or >> ) - Do not return local variable by reference (dangling reference)
L-Value and R-Value
- If you want the object returned by a function to be an L-value, it must be returned by reference
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| class Employee {
public:
Money& getSalary() {return salary;}
const Money& getSalaryNew() {return salary;}
...
private:
Money salary;
...
};
int main() {
Employee joe;
joe.getSalary().input();
//can access and manipulate private member variable
joe.getSalaryNew().input(); //illegal
|
- Returning a class-type can be efficient when return by reference
- But private member can be easily manipulated
const can protect this from happening
Overloading >> and <<
- Insertion operator,
<<- Used with
cout - A binary operator
cout<<"Hello"; : Firest operand is predefined object cout, second operand is string “Hello”
- What value should
<< return? : the cout object
1
2
3
4
5
6
7
8
| class Money
{
public:
friend ostream& operator <<(ostream& outputStream, const Money& amount);
friend istream& operator >>(istream& inputStream, Money& amount);
};
//Note : << and >> cannot be a member operator
//(the first operand is not the class obejct)
|
- Can chain
<< or >> because the operator returns by the reference - since
<< is not intended to change the original object, use const and call-by-reference while >> uses only call-by reference
Four types of returned value to use
- By plain old value
T f( ); - By const
const T f( ); - By reference
T& f( ); - By const reference
const T& f( );- For simple types , no point in using
const . - If you want the simple value returned to be l-value, then return by reference
1
2
3
4
| int foo();
const int foo(); //unnecessary
int& foo(); //returns aliases or reference
const int& foo(); //returns reference but cannot be modified via this function
|
- Returning a local variable by reference is problematic
const T and const T& are very similar : the const modifier- But using
const T is more preferred : because of hazard of dangling reference (local parameter returned by reference)
Assignment operator =
- Must be overloaded as a member operator
- Automatically overloaded
- The default assignment operator performs the member-wise copy.
- Member variables from one object → corresponding member variables from other
Increment and Decrement
- Two version : prefix, postfix
- For compilers to distinguish two verisons of operator, add dummy 2nd parameter of type
int
1
2
| IntPair operator ++(); //Prefix
IntPair operator ++(int); //Postfix
|
Overloading []
- Need to be a member function
- Need to return reference to use the operator at the left-hand side of
=
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| class CharPair
{
public:
char& operator[] (int index);
}
char& CharPair::operator[] (int index)
{
...
}
int main() {
CharPair a;
a[1]= 'A';
//For l-value, output must be reference
cout<<a[1];
}
|