This is a continuation from the previous Module. The source code for this tutorial is available inC++ Data Encapsulation source code.
The C++ programming abilities that should be acquired: Able to understand and use:
| 13.12 Operator Overloading
1. // program opverlod.cpp, operator overloading 2. #include <iostream> 3. using namespace std; 4. 5. // a class declaration part 6. class wall 7. { 8. public: 9. int length; 10. int width; 11. 12. public: 13. void set(int l, int w) {length = l; width = w;} 14. int get_area(void) {return length * width;} 15. // operator overloading 16. friend wall operator + (wall aa, wall bb); // add two walls 17. friend wall operator + (int aa, wall bb); // add a constant to a wall 18. friend wall operator * (int aa, wall bb); 19. // multiply a wall by a constant 20. }; 21. 22. // class implementation part 23. wall operator + (wall aa, wall bb) 24. // add two walls widths together 25. { 26. wall temp; 27. temp.length = aa.length; 28. temp.width = aa.width + bb.width; 29. return temp; 30. } 31. 32. wall operator + (int aa, wall bb) 33. // add a constant to wall 34. { 35. wall temp; 36. temp.length = bb.length; 37. temp.width = aa + bb.width; 38. return temp; 39. } 40. 41. wall operator * (int aa, wall bb) 42. // multiply wall by a constant 43. { 44. wall temp; 45. temp.length = aa * bb.length; 46. temp.width = aa * bb.width; 47. return temp; 48. } 49. 50. void main() 51. { 52. wall small, medium, large; //object instances 53. wall temp; 54. 55. small.set(2,4); 56. medium.set(5,6); 57. large.set(8,10); 58. 59. cout<<"Normal values\n"; 60. cout<<"-------------\n"; 61. cout<<"Area of the small wall surface is "<<small.get_area()<<"\n"; 62. cout<<"Area of the medium wall surface is "<<medium.get_area()<<"\n"; 63. cout<<"Area of the large wall surface is "<<large.get_area()<<"\n\n"; 64. cout<<"Overload the operators!"<<"\n"; 65. cout<<"-----------------------"<<endl; 66. temp = small + medium; 67. cout<<"New value-->2 * (4 + 6)\n"; 68. cout<<"New area of the small wall surface is "<<temp.get_area()<<"\n\n"; 69. cout<<"New value-->2 * (10 + 4) \n"; 70. temp = 10 + small; 71. cout<<"New area of the medium wall surface is "<<temp.get_area()<<"\n\n"; 72. cout<<"New value-->(4 * 8) * (4 * 10)\n"; 73. temp = 4 * large; 74. cout<<"New area of the large wall surface is "<<temp.get_area()<<"\n\n"; 75. 76. // system("pause"); 77. }
|

The end result is that objects of the new class can be used in as natural as the predefined types. In fact, they seem to be a part of the language rather than your own add-on.
In this case we overload the + operator and the* operator, with the declarations in lines 16 through 18 as shown below, and the definitions in lines 23 through 47. The methods are declared as friend functions, so we can use the double parameter function as listed.
If we do not use the friend construct, the function would be a part of one of the objects and that object would be the object to which the message was sent.
friend wall operator + (wall aa, wall bb); // add two walls
friend wall operator + (int aa, wall bb); // add a constant to a wall
friend wall operator * (int aa, wall bb);
By including the friend construct allows us to separate this method from the object and call the method with infix notation. Using this technique, it can be written as:
object1 + object2
Rather than:
object1.operator + (object2)
Also, without the friend construct we could not use an overloading with an int type variable for the first parameter because we can not send a message to an integer type variable such as
int.operator + (object)
Two of the three operators overloading use an int for the first parameter so it is necessary to declare them as friend functions.
There is no upper limit to the number of overloading for any given operator. Any number of overloading can be used provided the parameters are different for each particular overloading.
As shown below, the header in line 23, illustrates the first overloading where the+ operator is overloaded by giving the return type followed by the keyword operator and the operator we wish to overload.
wall operator + (wall aa, wall bb)
The two formal parameters and their types are then listed in the parentheses and the normal function operations are given in the implementation of the functions in lines 25 through 30 as shown below.
Notice that the implementation of the friend functions is not actually a part of the class because the class name is not prepended onto the method name in line 22.
{
wall temp;
temp.length = aa.length;
temp.width = aa.width + bb.width;
return temp;
}
There is nothing unusual about this implementation; it should be easily understood by you at this point. For purposes of illustration, some simple mathematical operations are performed in the method implementation, but any desired operations can be done.
The biggest difference occurs in line 65 as shown below where this method is called by using the infix notation instead of the usual message sending format.
temp = small + medium;
-------------------------------------------------------------------------------------------------------------------------
Infix Expression: Any expression in the standard form such as "4*2-6/3" is an Infix (In order) expression.
Postfix Expression: The Postfix (Post order) form of the above expression is "42*63/-".
--------------------------------------------------------------------------------------------------------------------------
Since thesmall and medium variables are objects of the wall class, the system will search for a way to use the + operator on two objects of wall class and will find it in the overloaded operator + method we have just discussed.
In line 70 as shown below, we ask the system to add an int type constant to a small object of classwall, so the system finds the other overloading of the + operator beginning in line 31 through 38 to perform this operation.
temp = 10 + small;
In line 72 as shown below, we ask the system to use the * operator to do something to an int constant and a large object of class wall, which it satisfies by finding the method in lines 40 through 47.
temp = 4 * large;
Note that it would be illegal to attempt to use the * operator the other way around, namely large * 4 since we did not define a method to use the two types in that order. Another overloading could be given with reversed types, and we could then use the reverse order in a program.
You will notice that when using operator overloading, we are also using function name overloading (function overloading) since some of the function names are same. When we use operator overloading in this manner, we actually make our programs look like the class is a natural part of the language since it is integrated into the language so well.
Each new part we study has its pitfalls which must be warned against and the part of operator overloading seems to have the record for pitfalls since it is so prone to misuse and has several problems.
The overloading of operators is only available for classes; you cannot redefine the operators for the predefined basic data types.
The preprocessing symbols # and ## cannot be overloaded.
The=, [ ],( ), and → operators can be overloaded only as non-static member functions. These operators cannot be overloaded for enum types. Any attempt to overload a global version of these operators results in a compile-time error.
The keywordoperator followed by the operator symbol is called the operator function name; it is used like a normal function name when defining a new (overloaded) action for the operator.
The operator function cannot alter the number of arguments or the precedence and associativity rules applying to normal operator use.
Table 13.1 and 13.2 list the operators that can be overloaded and cannot be overloaded respectively.
Which method is used is implementation dependent, so you should use them in such a way that it doesn’t matter which is used. Compile and run the programopverlod.cpp before continuing on the next program example.
Operators | ||||
+ | - | * | / | % |
^ | & | | | ~ | ! |
= | < | > | += | -= |
*= | /= | %= | ^= | &= |
|= | << | >> | >>= | <<= |
== | != | <= | >= | && |
|| | ++ | -- | ->* | , |
→ | [ ] | ( ) | new | delete |
Table 13.1: Operators that can be overloaded | ||||
Operators | |||
. | .* | ?: | :: |
Table 13.2: Operators that cannot be overloaded | |||
Examine the program named funovlod.cpp. This is an example of function name overloading within a class. In this program, the constructor is overloaded as well as one of the methods to illustrate what can be done.
1. // program funovlod.cpp, function overloading
2. #include <iostream>
3. using namespace std;
4.
5. // a class declaration part
6. class many_names
7. {
8. int length;
9. int width;
10. public:
11. many_names(void);
12. many_names(int len);
13. many_names(int len, int wid);
14. // constructors with different number and type
15. // of parameter list – overloaded functions
16. void display(void);
17. void display(int one);
18. void display(int one, int two);
19. void display(float number);
20. // methods with different number and type
21. // of parameter list – overloaded functions
22. };
23.
24. // implementation part
25. many_names::many_names(void) // void
26. {
27. length = 8;
28. width = 8;
29. }
30.
31. many_names::many_names(int len) // one parameter
32. {
33. length = len;
34. width = 8;
35. }
36.
37. many_names::many_names(int len, int wid) // two parameter
38. {
39. length = len;
40. width = wid;
41. }
42.
43. void many_names::display(void) // void for display
44. {
45. cout<<"From void display function, Area = "<<length * width<<"\n";
46. }
47.
48. void many_names::display(int one) // 1 parameter
49. {
50. cout<<"From int display function, Area = "<<length * width<<"\n";
51. }
52.
53. void many_names::display(int one, int two) // 2 parameters
54. {
55. cout<<"From two int display function, Area = "<<length * width<<"\n";
56. }
57.
58. void many_names::display(float number) //1 parameter
59. {
60. cout<<"From float display function, Area = "<<length * width<<"\n";
61. }
62.
63. // the main program
64. main()
65. {
66. many_names small, medium(10), large(12,15);
67. int gross = 144;
68. float pi = 3.1415, payroll = 12.50;
69.
70. cout<<"Guess, which function that they invoked???\n";
71. cout<<"------------------------------------------\n";
72. cout<<"-->small.display()\n";
73. small.display();
74. cout<<"\n-->small.display(100)\n";
75. small.display(100);
76. cout<<"\n-->small.display(gross,100)\n";
77. small.display(gross,100);
78. cout<<"\n-->small.display(payroll)\n";
79. small.display(payroll);
80. cout<<"\n-->medium.display()\n";
81. medium.display();
82. cout<<"\n-->large.display(pi)\n";
83. large.display(pi);
84.
85. // system("pause");
86. }

many_names small, medium(10), large(12, 15);
|
As many overloading as desired can be used provided that all of the parameter patterns are unique.
Other example is the << operator which is part of thecout class, which operates as an overloaded function since the way it outputs data is a function of the type of its input variable or the field we ask it to display.
Many programming languages have overloaded functions so you can output any data with the same function name. Compile and run this program.
Examine the program deftmeth.cpp carefully, illustrating those methods provided by the compiler, and why you sometimes can’t use the defaults but need to write your own, to do the job the defaults were intended to do for you.
1. // program deftmeth.cpp, default method
2. #include <iostream>
3.
4. #include <string> // or try #include <cstring>
5. using namespace std;
6.
7. // a class declaration part
8. class def
9. {
10. int size; // a simple stored value
11. char *string; // a name for the stored data
12. public:
13. // this overrides the default constructor
14. def(void);
15.
16. // this overrides the default copy constructor
17. def(def &in_object);
18.
19. // this overrides the default assignment operator
20. def &operator=(def &in_object);
21.
22. //This destructor should be required with dynamic allocation
23. ~def(void);
24.
25. // and finally, a couple of ordinary methods
26. void set_data(int in_size, char *in_string);
27. void get_data(char *out_string);
28. };
29.
30. // class implementation
31. def::def(void)
32. {
33. size = 0;
34. string = new char[2];
35. strcpy(string, "");
36. }
37.
38. def::def(def &in_object)
39. {
40. size = in_object.size;
41. string = new char[strlen(in_object.string) + 1];
42. strcpy(string, in_object.string);
43. }
44.
45. def& def::operator=(def &in_object)
46. {
47. delete [ ] string;
48. size = in_object.size;
49. string = new char[strlen(in_object.string) + 1];
50. strcpy(string, in_object.string);
51. return *this; // this pointer
52. }
53.
54. def::~def(void)
55. {
56. delete [] string;
57. }
58.
59. void def::set_data(int in_size, char *in_string)
60. {
61. size = in_size;
62. delete [ ] string;
63. string = new char[strlen(in_string) + 1];
64. strcpy(string, in_string);
65. }
66.
67. void def::get_data(char *out_string)
68. {
69. char temp[10];
70. strcpy(out_string, string);
71. strcat(out_string, " = ");
72. itoa(size, temp, 10);
73. strcat(out_string, temp);
74. }
75.
76. // the main program
77. void main()
78. {
79. char buffer[80];
80. def my_data;
81. my_data.set_data(8, " small size, override default constructor ");
82. my_data.get_data(buffer);
83. cout<<" content of buffer!!\n"<<buffer<<"\n";
84.
85. def more_data(my_data);
86. more_data.set_data(12, " medium size, override copy constructor");
87. my_data.get_data(buffer);
88. cout<<"\n content of buffer 2nd round!!\n"<<buffer<<"\n";
89.
90.
91. my_data = more_data;
92. my_data.get_data(buffer);
93. cout<<"\n content of buffer 3rd round, assignment
94. overload!!\n"<<buffer<<"\n";
95.
96. // system("pause");
97. }

Even if you do not include any constructors or operator overloading, you get a few defined automatically by the compiler.
Before we actually look at the program, a few rules must be followed in order to deliver a useful implementation such as:
If no constructors are defined by the writer of a class, the compiler will automatically generate a default constructor and a copy constructor. Both of these constructors will be defined for you later.
If the class author includes any constructor in the class, the default constructor will not be supplied by the constructor.
If the class author does not include a copy constructor, the compiler will generate one, but if the writer includes a copy constructor, the compiler will not generate one automatically.
If the class author includes an assignment operator, the compiler will not include one automatically; otherwise it will generate a default assignment operator.
Any class declared and used in a C++ program must have some way to construct an object because the compiler, by definition, must call a constructor when we instantiate an object. If we don’t provide a constructor, the compiler itself will generate one that it can call during construction of the object.
This is thedefault constructor, it is compiler work and we have used it implicitly in our programs examples. The default constructor does not initialize any of the member variables, but it sets up all the internal class references it needs, and calls the base constructor or constructors if they exist.
Base constructor is a constructor in the base class, and will be discussed ininheritance Module.
Line 14 of the program declares a default constructor as shown below which is called when you instantiate an object with no parameters.
def(void);
In this case, the constructor is necessary because we have an embedded string in the class that requires a dynamic allocation and an initialization of the string pointer to the NULL.
It will take little thought to see that our constructor is much better than the default constructor which would leave us with an uninitialized pointer. The default constructor is used in line 81 of this program example (8 instead of 12) as shown below.
my_data.set_data(8, " small size, override default constructor ");
The copy constructor is generated automatically for you by the compiler if you fail to define one. It is used to copy the contents of an object to a new object during construction of that new object.
If the compiler generates it for you, it will simply copy the contents of the original into the new object byte by byte, which may not be what you want.
For simple classes with no pointers, that is usually sufficient, but in our program example, we have a pointer as a class member so a byte by byte copy would copy the pointer from one to the other and they would both be pointing to the same allocated member.
For this reason, we declared our own copy constructor in line 16 as shown below.
// this overrides the default copy constructor
def(def &in_object);
...
And implemented in lines 38 to 43 as shown below.
def::def(def &in_object)
{
size = in_object.size;
string = new char[strlen(in_object.string) + 1];
strcpy(string, in_object.string)
}
...
A careful study of the implementation will reveal that the new class will indeed be identical to the original, but the new class has its own string to work with.
Since both constructors contain dynamic allocation, we must assure that the allocated data is destroyed when we are finished with the objects, so a destructor is mandatory as implemented in lines 54 through 57 as shown below.
def::~def(void)
{
delete [ ] string;
}
...
And the copy constructor is used in line 86 as shown below.
more_data.set_data(12, " medium size, override copy constructor");
Generally, copy constructors are invoked whenever a copy of an object is needed such as a function call by value, when returning an object from a called function or when initializing an object to be a copy of another object of the same class.
Also, an assignment operator is required for this program, because the default assignment operator simply copies the source object to the destination object byte by byte. This would result in the same problem we had with copy constructor.
The assignment operator is declared in line 20 as shown below:
// this overrides the default assignment operator
def &operator=(def &in_object);
...
And defined in lines 45 through 52 as shown below:
def& def::operator=(def &in_object)
{
delete [ ] string;
size = in_object.size;
string = new char[strlen(in_object.string) + 1];
strcpy(string, in_object.string);
return *this;
}
...
Here, we de-allocate the old string in the existing object prior to allocating room for the new text and copying the text from the source object into the new object. The assignment operator is used in line 91 as shown below.
my_data = more_data;
It should be fairly obvious to you that when a class is defined which includes any sort of dynamic allocation, the above three methods should be included in addition to the proper destructor.
If any of the four entities are omitted, the program may have terribly erratic behavior. Compile and run this program example.
tenouk C++ encapsulation code samples