08 Operator Overloading
08 Operator Overloading
Operator Overloading
Yean-Ru Chen
EE, NCKU
• This lecture shows how to enable C++’s operators to work with objects—a
process called operator overloading.
• One example of an overloaded operator built into C++ is <<, which is used
both as the stream insertion operator and as the bitwise left-shift operator.
• C++ overloads the addition operator (+) and the subtraction operator (-).
These operators perform differently, depending on their context in integer,
floating-point and pointer arithmetic.
2
Fundamentals of Operator Overloading
t3 t1 t2
Time t1, t2, t3;
Hour Hour Hour
t3 = t1 + t2;
Minute = Minute + Minute
Second Second Second
3
Fundamentals of Operator Overloading (cont.)
4
Fundamentals of Operator Overloading (cont.)
5
Fundamentals of Operator Overloading
(cont.)
• To use an operator on class objects, that operator must be
overloaded—with three exceptions.
• The assignment operator (=) may be used with every class to
perform member-wise assignment of the class’s data members.
• Dangerous for classes with pointer members; we’ll explicitly
overload the assignment operator for such classes.
HugeInt i, j; i = j;
• The address (&) and comma (,) operators may also be used with
objects of any class without overloading.
HugeInt i; HugeInt* ptr = &i;
• The address operator returns a pointer to the object.
• The comma operator evaluates the expression to its left then
the expression to its right, and returns the value of the latter
expression.
for (HugeInt i = 0, j=1; i<10; k=(i++, j++)) ;
6
Overloading Stream Insertion and Stream
Extraction Operators
• You can input and output fundamental-type data using the stream
extraction operator >> and the stream insertion operator <<.
• You can also overload these operators to perform input and output for
your own types.
7
Storing a Phone Number
1 #include <iostream>
2 #include "PhoneNumber.h"
3 using namespace std;
4
5 int main()
6 {
7 PhoneNumber phone;
8 cout << "Enter your phone number as (XX) XXXXXXX: ";
9 cin >> phone;
10 cout << phone << endl;
11 return 0;
12 }
8
PhoneNumber.h and PhoneNumber.cpp
PhoneNumber.h PhoneNumber.cpp
1 #include <iomanip>
1 #ifndef PHONENUMBER_H
2 #include "PhoneNumber.h"
2 #define PHONENUMBER_H
3 using namespace std;
3 #include <iostream>
4 ostream & operator <<(ostream &out,
4 #include <string>
const PhoneNumber &num)
5 using namespace std;
5 {
6 class PhoneNumber {
6 out << "(" << num.areaCode << ") "
7 friend ostream &operator <<
<< num.exchangeNum << "-"
(ostream&, const PhoneNumber&);
<< num.serialNum;
8 friend istream &operator >>
7 return out;
(istream &, PhoneNumber &);
8 }
9 private:
9 istream &operator >> (istream & in,
10 string areaCode;
PhoneNumber & num)
11 string exchangeNum;
10 {
12 string serialNum;
11 in.ignore(); // skip (
13 };
12 in >> setw(2) >> num.areaCode;
14 #endif
13 in.ignore(2); // skip ) and space
9 cin >> phone; 14 in >> setw(3) >> num.exchangeNum;
15 in >> setw(4) >> num.serialNum;
10 cout << phone << endl;
16 return in; 9
17 }
Stream Extraction Operator >>
10
Stream Extraction Operator >> (cont.)
11 in.ignore(); // skip (
12 in >> setw(2) >> num.areaCode;
13 in.ignore(2); // skip ) and space
11
Stream Extraction Operator >> (cont.)
15
Restrictions on Operator Overloading (cont.)
16
Restrictions on Operator Overloading (cont.)
17
Restrictions on Operator Overloading (cont.)
• You cannot, for example, change the meaning of how + adds two
integers.
19
Operator Functions as Class Members vs.
Global Functions
21
Operator Functions as Class Members vs.
Global Functions (cont.)
• To use the operator in this manner where the right operand is an object
of a user-defined class, it must be overloaded as a global function.
22
Operator Functions as Class Members vs.
Global Functions (cont.)
• The global function simply swaps its arguments and calls the member
function.
member function global function
int Cls::operator+(int x) int operator+(int x, Cls obj)
{ {
… return obj + x;
} }
23
Making + Commutative
24
Dynamic Memory Management
25
Dynamic Memory Management (cont.)
• Once memory is allocated in the free store, you can access it via the
pointer that operator new returns.
• You can return memory to the free store by using the delete operator
to deallocate it.
• The new operator allocates storage of the proper size for an object of
the specified type, calls the constructor to initialize the object and
returns a pointer to the type specified.
26
Dynamic Memory Management (cont.)
• To destroy a dynamically allocated object and free the space for the
object, use the delete operator as follows:
• delete ptr;
• This statement first calls the destructor for the object to which ptr
points, then deallocates the memory associated with the object,
returning the memory to the free store.
27
Sample Program with Memory Leak
1 #include <iostream>
2 using namespace std;
3 int main()
4{
5 int x, *y;
6 for (x = 0; x < 10000000; x++)
7 y = new int;
8 cin >> x;
9 return 0;
10 }
Output of top:
PID USERNAME THR PRI NICE SIZE RES STATE TIME WCPU COMMAND
71685 yeanru 1 5 0 157M 156M ttyin 0:02 42.78% memory_leak
28
Operator new
29
Operator new [ ]
30
Operator delete [ ]
• If the pointer points to an array of objects, the statement first calls the
destructor for every object in the array, then deallocates the memory.
• Using delete on a null pointer (i.e., a pointer with the value 0) has
no effect.
31
Common Errors on Using Operator delete
and delete [ ]
32
Error: Using Operator delete to Delete an
Array
1 #include <iostream>
2 using namespace std;
3 class Cls {
4 public:
5 ~Cls() { cout << "Destructor" << endl; }
6 private:
7 int x; Output:
8 }; Destructor
9 int main()
10 {
11 Cls * ptr = new Cls[10];
12 delete ptr;
13 return 0;
14 }
33
Error: Using Operator delete [ ] to Delete
an Object (cont.)
Output:
1 #include <iostream> Destructor
2 using namespace std;
Destructor
3 class Cls {
4 public: Destructor
5 ~Cls() { cout << "Destructor" << endl; } Destructor
6 private: Destructor
7 int x; Destructor
8 }; Destructor
9 int main() Destructor
10 { Destructor
11 Cls * ptr = new Cls; Destructor
12 delete [] ptr;
Destructor
13 return 0;
14 } Destructor
Destructor
Destructor
Aborted (core dumped)
34
Case Study: Array Class
• Pointer-based arrays have many problems, including:
• A program can easily “walk off” either end of an array, because C++ does not check
whether subscripts fall outside the range of an array.
• When an array is passed to a function designed to handle arrays of any size, the
array’s size must be passed as an additional argument.
35
Case Study: Array Class (cont.)
36
myArray.cpp
1 #include <iostream> 20 if (ints1 != ints2)
2 #include "Array.h" 21 cout << "ints1 != ints2" << endl;
3 using namespace std; 22
4 int main()
5{ 23 Array ints3(ints1);
6 Array ints1(7); 24 cout << "ints3: " << ints3 << endl;
7 Array ints2; 25
8
26 ints1 = ints2;
9 cout << "Size of ints1 = " << ints1.getSize();
10 cout << "\ncontent = " << ints1; 27 cout << "ints1: " << ints1 << endl;
11 cout << "Size of ints2 = " << ints2.getSize(); 28 cout << "ints2: " << ints2 << endl;
12 cout << "\ncontent = " << ints2; 29
13
30 cout << ints1[5] << endl;
14 cout << "\nEnter 17 numbers: " << endl;
15 cin >> ints1 >> ints2; 31 ints1[5] = 50;
16 32 cout << "ints1: " << ints1 << endl;
17 cout << "ints1: " << ints1 << endl; 33
18 cout << "ints2: " << ints2 << endl;
34 ints1[20] = 60;
19
35 return 0;
37
36 }
Array.h
1 #ifndef ARRAY_H 20 int & operator[](int);
2 #define ARRAY_H 21 int operator[](int) const;
3 #include <iostream> 22 private:
4 using namespace std;
5 class Array{ 23 int size;
6 friend ostream & operator<< 24 int *ptr;
(ostream &, const Array &); 25 };
7 friend istream & operator>> 26 #endif
(istream &, Array &);
8 public:
A=B=C 的作法是:
9 Array (int = 10);
10 Array (const Array &); 先把 (B=C)做完, 再做 (A=B). 當做 (A=B)時,
11 ~Array(); B是在 Rvalue, 是不會被改到的. 這時加
12 int getSize() const; 不加 const好像都可以.
13 但是如果寫 (A=B)=C, 括弧會先做的話,
14 const Array & operator = (const Array &); 就會先做 (A=B), 再把C assign給 (A=B)的
15 bool operator== (const Array &) const; 結果. 這時, (A=B)的結果是在 Lvalue! 這
16 bool operator!=(const Array & right) const 時候就會被改到. 但我們不希望用
17 { (A=B)=C這種 assign 的寫法, 所以在
18 return !(*this == right);
return type上加用
38 const 來限制它
19 }
回想:
Array.cpp int a; //寫 &a 是
取a的位址.
ints1: 8 9 10 11 12 13 14 15 16 17
ints2: 8 9 10 11 12 13 14 15 16 17
13
ints1: 8 9 10 11 12 50 14 15 16 17
42
Array’s Copy Constructor
44
When = used in declaration
Array’s Copy Constructor (cont.)
46
Error: Pass-by-value in Copy Constructor
1 #include <iostream>
2 using namespace std;
3 class Cls {
4 public:
5 Cls():x(5) {}
6 Cls(Cls obj) { x = obj.x; }
7 private:
8 int x;
9 };
10 int main()
11 {
12 Cls obj1; $ g++ -o copy_constructor copy_constructor.cpp
13 Cls obj2 (obj1); copy_constructor.cpp:6: error: invalid constructor; you
14 return 0; probably meant `Cls (const Cls&)'
15 } copy_constructor.cpp:6: error: invalid member function
declaration
47
Array’s Copy Constructor (cont.)
48
Dangling Pointer
49
Array’s operators << and >>
50
Array’s Operator =
52
Notice on Class with Dynamically Allocated
Memory
5 class Array{
…
8 private: In main function:
10 Array (const Array &); 23 Array ints3(ints1);
… …
14 const Array & operator = (const Array 26 ints1 = ints2;
&);
53
Array’s Operator ==
54
Array’s Operator !=
5 class Array{
…
15 bool operator== (const Array &) const;
16 bool operator!=(const Array & right) const
17 {
18 return !(*this == right);
19 }
55
Array’s operator [ ]
• The array subscript operator [] is not restricted for use only with
arrays; it also can be used, for example, to select elements from other
kinds of container classes, such as linked lists, strings and dictionaries.
56
Array’s operator [ ] (cont.)
57
Overloading ++ and --
58
Preincrementing ++
59
Preincrementing ++ (cont.)
60
Postincrementing ++
• The convention that has been adopted in C++ is that, when the
compiler sees the postincrementing expression d1++, it
generates the member function call
• d1.operator++( 0 )
61
Postincrementing ++ (cont.)
• operator++( d1, 0 )
62
Postincrementing ++ (cont.)
• The extra object that is created by the postfix increment (or decrement)
operator can result in a significant performance problem-especially
when the operator is used in a loop. For this reason, you should use the
postfix increment (or decrement) operator only when the logic of the
program requires postincrementing (or postdecrementing).
63
Date.h
1 #ifndef DATE_H
2 #define DATE_H
3 #include <iostream>
4 using namespace std;
5 class Date {
6 friend ostream & operator<<(ostream&,const Date&);
7 public:
8 Date(int m=1, int d=1, int y=1900);
9 void setDate(int, int , int);
10 Date &operator++(); // for ++d
11 Date operator++(int); // for d++ (D += 7) += 10
12 const Date &operator+=(int); // for d += n 這種寫法不可以
13 static bool leapYear(int);
14 bool endOfMonth(int) const;
15 private:
16 int month, day, year;
17 static const int days[];
18 void helpIncrement();
19 };
20 #endif
64
int x = 1; int a =1; int b = 1;
Date.cpp 那 x = a++ + b;
會得到什麼?
• 目前使用的格里曆閏年規則如下:
– 西元年份除以4不可整除,為平年。
– 西元年份除以4可整除,且除以100不可整除,為閏年。
– 西元年份除以100可整除,且除以400不可整除,為平年
– 西元年份除以400可整除,為閏年。
• 所以, 定義閏年: 四年一閏, 遇到100不閏, 400又要閏
• 一個回歸年實際上是 365.2421990741日 = 365天5小時48分
46秒
– 每四年一閏, 是以 365.25天/年來算. 所以這種閏法, 會造成一年多
出0.0078天.
– 最後天文計算的作法是: 4年一閏, 但400年只能97閏.
Date.cpp (cont.)
37 bool Date::leapYear(int y) 57 if(month<12)
38 { 58 {
39 if(y % 400 == 0 || 59 ++month;
40 (y % 100 != 0 && y % 4 == 0)) 60 day=1;
41 return true; 61 }
42 else 62 else
43 return false; 63 {
44 } 64 ++year;
45 bool Date::endOfMonth(int d) const 65 month=1;
46 { 66 day=1;
47 if(month == 2 && leapYear(year)) 67 }
48 return d == 29; 68 }
49 else
50 return d == days[month];
51 }
52 void Date::helpIncrement()
53 {
54 if(!endOfMonth(day))
55 ++day;
56 else
67
Date.cpp (cont.)
69 ostream& operator<<(ostream & out,const Date & d)
70 {
71 static string monthName[13] =
72 {"", "January", "February",
73 "March", "April", "May",
74 "June", "July", "August",
75 "September", "October",
76 "November", "December" };
77 out << monthName[d.month] << ' ' << d.day << ", " << d.year;
78 return out;
79 }
68
myDate.cpp
1 #include <iostream> 22
2 #include "Date.h" 23 cout << "d4: "<< d4 << endl;
3 using namespace std;
24 cout << "d4++: "<< d4++ << endl;
4 int main() 25 cout << "d4: "<< d4 << endl;
5{ 26
6 Date d1; 27 return 0;
7 Date d2(12,27,1992); 28 }
8 Date d3(0,99,8045); d1: January 1, 1900
9 cout << "d1: "<< d1 << endl; d2: December 27, 1992
10 cout << "d2: "<< d2 << endl; d3: January 1, 1900
11 cout << "d3: "<< d3 << endl; d2 += 7: January 3, 1993
12 cout << "d2 += 7: "<< (d2+=7) << endl; d3: February 28, 1992
13 ++d3: February 29, 1992
14 d3.setDate(2,28,1992); d4: July 13, 2002
15 cout << "d3: "<< d3 << endl; ++d4: July 14, 2002
16 cout << "++d3: "<< ++d3 << endl; d4: July 14, 2002
17 d4: July 14, 2002
18 Date d4(7,13,2002); d4++: July 14, 2002
19 cout << "d4: "<< d4 << endl; d4: July 15, 2002
20 cout << "++d4: "<< ++d4 << endl;
21 cout << "d4: "<< d4 << endl; 69
Case Study: A Date Class
70
Case Study: A Date Class (cont.)
71
Standard Library Class string
72
Using Operators on string Objects
1 #include <iostream> 22
2 #include <string> 23 s1+=s2;
3 using namespace std; 24 cout << "\ns1: " << s1;
4 int main() 25 s1+= "to you";
5{ 26 cout << "\ns1: " << s1;
6 string s1("happy"); 27 cout << "\ns1.substr(0,14): " << s1.substr(0,14);
7 string s2("birthday"); 28 cout << "\ns1.substr(15): " << s1.substr(15);
8 string s3; 29
9 cout << "s1: " << s1;
10 cout << "\ns2: " << s2;
11 cout << "\ns3: " << s3;
12 cout << "\ns2 == s1?: " << ((s2==s1)?"true":"false");
13 cout << "\ns2 != s1?: " << ((s2!=s1)?"true":"false");
14 cout << "\ns2 > s1?: " << ((s2>s1)?"true":"false");
15 cout << "\ns2 < s1?: " << ((s2<s1)?"true":"false");
16 cout << "\ns2 >= s1?: " << ((s2>=s1)?"true":"false");
17 cout << "\ns2 <= s1?: " << ((s2<=s1)?"true":"false");
18
19 cout << “\nIs s3 empty?: ” << ((s3.empty())?"true":"false");
20 s3=s1;
21 cout << "\ns3: " << s3; 73
Using Operators on string Objects (cont.)
75
Using Operators on string Objects (cont.)
76