We are able to define our own types using classes. Sometimes we may want the class types to work with operators in a manner that we use with primitive types.
The "this" keyword is used to denote a pointer to the class object and can be used only inside the class.
Ex:
#include <iostream>using namespace std ;class point { public: int x1 ; int y1 ; point( int x1 , int y1 ) { this->x1 = x1 ; this->y1 = y1 ; }};int main(){ return 0 ;}
The "this" is a pointer to the object and in the above code we are using it to refer to the variables "x1" and "y1" in the class as the variables in the argument are also called "x1" and "y".
#include <iostream>using namespace std ;class point { public: int x1 ; int y1 ; point operator+( const point& param1 );}; point point::operator+( const point& param1 ){ cout << "Calling operator + member function." << endl ; point pointObj ; pointObj.x1 = x1 + param1.x1 ; pointObj.y1 = y1 + param1.y1 ; return pointObj ;}int main(){ point point1 ; point1.x1 = 1 ; point1.y1 = 1 ; point point2 ; point2.x1 = 2 ; point2.y1 = 2 ; point point3 ; point3 = point1 + point2 ; cout << point3.x1 << " " << point3.y1 << endl ; return 0 ;}
In the above we have a point class that represents a 2 dimensional co-ordinate. We want to be able to add 2 objects of the point class by using an expression of the form:
point1 + point 2
We define a member function with the signature:
point operator+( const point& param1 );
The method name is "operator+" and it contains an argument that takes a point object. We can specify different arguments as well as a different return type also.
Inside this function we create a new object that is returned. The function gets invoked when we execute the statement:
point3 = point1 + point2 ;
The member function "operator+" gets called for the object "point1" and the object "point2" gets passed in as the argument. The following list shows the symbols that we can use for operator overloading.
Let's say that we want to be able to do something like:
point + 5
The integer 5 will be added to the x-coordinate and the y-ordinate. We can add another member function.
#include <iostream>using namespace std ;class point { public: int x1 ; int y1 ; point operator+( const point& param1 ); point operator+( int param1 );}; point point::operator+( const point& param1 ){ cout << "Calling operator + member function." << endl ; point pointObj ; pointObj.x1 = x1 + param1.x1 ; pointObj.y1 = y1 + param1.y1 ; return pointObj ;} point point::operator+( int param1 ){ cout << "Calling operator + member function with an integer argument." << endl ; point pointObj ; pointObj.x1 = x1 + param1 ; pointObj.y1 = y1 + param1 ; return pointObj ;}int main(){ point point1 ; point1.x1 = 1 ; point1.y1 = 1 ; point point2 ; point2.x1 = 2 ; point2.y1 = 2 ; point point3 ; point3 = point1 + point2 ; cout << point3.x1 << " " << point3.y1 << endl ; point3 = point1 + 5 ; cout << point3.x1 << " " << point3.y1 << endl ; return 0 ;}
Output:
$ g++ oo1a.cpp ; ./a.exe
Calling operator + member function.
3 3
Calling operator + member function with an integer argument.
6 6
We can see that "C++" is quite flexible as far as the operator and the arguments. There is another way we could have created the "+" function and that is by making the function global ( not inside the class ) and making it into a friend.
#include <iostream>using namespace std ;class point { public: int x1 ; int y1 ; friend point operator+( const point& param1 , const point& param2 ) ;}; point operator+( const point& param1 , const point& param2 ){ cout << "Calling operator +" << endl ; point pointObj ; pointObj.x1 = param1.x1 + param2.x1 ; pointObj.y1 = param1.y1 + param2.y1 ; return pointObj ;}int main(){ point point1 ; point1.x1 = 1 ; point1.y1 = 1 ; point point2 ; point2.x1 = 2 ; point2.y1 = 2 ; point point3 ; point3 = point1 + point2 ; cout << point3.x1 << " " << point3.y1 << endl ; return 0 ;}
Output:
$ ./a.exe
Calling operator +
3 3
When we call the statement
point3 = point1 + point2 ;
the global function :
point operator+( const point& param1 , const point& param2 )
is invoked . Since it's a global function both the "pint1" and "point2" objects are passed to the function. We can use the member function or the global function approach to implement the "+" operator. Which one should we use ? If an operation can be done using the class member function then we should use that. This reduces the number of global functions that are introduced. Sometimes the argument on the left hand side is not that of the class we want to implement the operator for and in that case we have no choice but to use the global function.
Wouldn't it be nice if we just use
cout << pointObject ;
syntax to print out the contents of the "point" class. We can overload the operator "<<" but since "cout" is not of the point class we have to use a global function.
The "cout" is an object of the "ostream" class and this class also has overloaded operators for the "<<" operator. If we write something such as:
cout << "table" << 5 << endl ;
The member function gets called for :
cout << "table"
This will pass the string "String" to the cout member function and that function will print the "table" and return the same "cout" object. Now we have something like:
table
cout << 5 << endl ;
We have printed "table" on the screen and now we have the same cout object ready to work on the next argument which happens to be 5. Let us implement a global function for our point class.
Example:
#include <iostream>using namespace std ;class point { public: int x1 ; int y1 ; point operator+( const point& param1 ); friend ostream& operator<<(ostream& os, const point& param1) ;}; point point::operator+( const point& param1 ){ cout << "Calling operator + member function." << endl ; point pointObj ; pointObj.x1 = x1 + param1.x1 ; pointObj.y1 = y1 + param1.y1 ; return pointObj ;}ostream& operator<<(ostream& os, const point& param1){ os << param1.x1 << " " << param1.y1 << endl ; return os;}int main(){ point point1 ; point1.x1 = 1 ; point1.y1 = 1 ; point point2 ; point2.x1 = 2 ; point2.y1 = 2 ; cout << point1 << endl ; cout << point2 << endl ; return 0 ;}
Output:
$ ./a.exe
1 1
2 2
Using the subscript operator.
Let's say we had a class and we wanted to give the user the ability to use the array index operator on our class. We can do that by overloading the array index operator.
Ex:
#include <iostream>using namespace std ;class myVector { public: int* ptr ; int size ; myVector(int sizeP ) { size = sizeP ; ptr = new int[sizeP] ; for( int i1=0 ; i1< size ; i1++ ) { ptr[i1] = i1+1 ; } } ~myVector() { if ( ptr != NULL ) delete[] ptr ; } int& operator[] (int index) { return ptr[index] ; } void print() { for( int i1=0 ; i1<size ; i1++ ) cout << ptr[i1] << " " ; cout << endl ; }} ;int main(){ myVector obj1(2) ; obj1.print() ; obj1[0] = 100 ; obj1.print() ; return 0 ;}
Output:
$ g++ vector.cpp ; ./a.exe
1 2
100 2
Exercises:
1) The prefix operator is of the form:
operator++()
and the postfix operator is of the form:
operator++( int )
For the point class assume that these operators means we add 1 to x1 and 1 to y1. Implement these operators and run your code. Make sure you get the same output as shown below.
File "ex1.cpp"
#include <iostream>using namespace std ;class point { public: int x1 ; int y1 ; point operator+( const point& param1 ); friend ostream& operator<<(ostream& os, const point& param1) ; //TO DO Add the post fix and pre fix increment operators}; point point::operator+( const point& param1 ){ cout << "Calling operator + member function." << endl ; point pointObj ; pointObj.x1 = x1 + param1.x1 ; pointObj.y1 = y1 + param1.y1 ; return pointObj ;}ostream& operator<<(ostream& os, const point& param1){ os << param1.x1 << " " << param1.y1 << endl ; return os;}int main(){ point point1 ; point1.x1 = 1 ; point1.y1 = 1 ; point point2 ; point2.x1 = 2 ; point2.y1 = 2 ; point point3 ; point3 = ++point1 ; cout << point3 << endl ; point3 = point1++ ; cout << point3 << endl ; return 0 ;}
Output:
$ g++ ex1.cpp ; ./a.exe
2 2
3 3
Bonus ) Try implementing the prefix part and then call the prefix from the postfix operator. Do you get the same results.
2)
We created the copy constructor for our "mystring" class previously. If we didn't have a copy constructor then we run into memory delete issues. The same problem happens when we use the default assignment operator. If we have 2 objects of mystring such as "mystring1" and "mystring2" then:
mystring2 = mystring1
leads to the same problem. Also we want to be able to add 2 mystring objects using the "+" operator such as:
mystring1 + mystrin2
Implement 3 additional functions : assignment operator, + operator and the "<<" operator .
Remember the assignment operator should return an object and not void so that we can use it in situations like:
mystring3 = ( mystring2 = mystring1 ) ;
We can do that by returning the object "*this" .
A function "append" has been written out for you.
File: "mystring.h"
#ifndef MYSTRING_H#define MYSTRING_H#include <string.h>#include <iostream>using namespace std ;class mystring { private: char* ptr ; public: mystring( ) ; mystring( const char* str1 ) ; mystring(const mystring& mystringObj) ; ~mystring() ; void print() ; void append( const mystring& mystringObj ) ; //TODO Write the body for the below functions mystring operator+( const mystring& param1 ); mystring operator=( const mystring& param1 ); friend ostream& operator<<(ostream& os, const mystring& param1) ;};#endif
File: "mystring.cpp"
#include "mystring.h" mystring::mystring( ) { ptr = NULL ; } mystring::mystring( const char* str1 ) { cout << "Inside the constructor with literal string." << endl ; if ( str1 == NULL ) ptr = NULL ; else { int len = strlen( str1 ) + 1; ptr = new char[ len ] ; strcpy ( ptr, str1 ) ; } } mystring::mystring(const mystring& mystringObj) { cout << "Inside the copy constructor." << endl ; //ptr1 = new char ; if (mystringObj.ptr != NULL ) { int len = strlen(mystringObj.ptr) + 1 ; ptr = new char[ len ] ; strcpy ( ptr, mystringObj.ptr ) ; } else ptr = NULL ; } mystring::~mystring() { cout << "Inside the destructor." << endl ; if ( ptr != NULL ) delete[] ptr ; } void mystring::print() { cout << ptr << endl ; } void mystring::append( const mystring& mystringObj ) { if ( mystringObj.ptr == NULL ) return ; //cout << "Step1" << endl ; if( ptr == NULL ) { //cout << "Step2" << endl ; int len = strlen(mystringObj.ptr) + 1 ; ptr = new char[ len ] ; strcpy ( ptr, mystringObj.ptr ) ; } else { //cout << "Step3" << endl ; int len = strlen(ptr) + strlen(mystringObj.ptr) + 1 ; //cout << "len:" << len << endl ; char* ptr1 = new char[ len ] ; strcpy( ptr1 , ptr ) ; delete ptr ; // cout << "Step4" << endl ; strcat( ptr1 , mystringObj.ptr ) ; ptr = ptr1 ; } } mystring mystring::operator+( const mystring& param1 ) { } mystring mystring::operator=( const mystring& param1 ) { } ostream& operator<<(ostream& os, const mystring& param1) { }
File: "main.cpp"
#include "mystring.h"int main(){ //Make sure append works. mystring str1 ; str1.append( "Marvin " ) ; cout << str1 << endl ; str1.append( "Hagler " ) ; cout << str1 << endl ; mystring str2("Thomas Hearns" ) ; str2 = str1 ; cout << "str2:" << str2 ; mystring str4 ; mystring str3("Thomas Hearns" ) ; str4 = str1 + str3 ; cout << "str4:" << str4 << endl ; return 0 ;}
Bonus
Add the subscript operator to access or change the character in the string.
End
Casting