Comp Science - C++

Operating Overloading

Operator Overloading is generally used with classes since the operators are already defined for the primitive data types. Operators are pretty much the same as functions. They take parameters and have a return value. For example the plus operator is a binary operator and therefore takes two parameters, usually of the same type , and it returns the result of the same type as well. So 1+2 returns 3.

To get started overloading an operator, let's create a simple class, cat. We will then overload its stream insertion operator , <<.

class cat
{
int ID;
char * name;
char * breed;
public:
~cat(){delete[]name; delete[]breed;};
cat(int ID, char * name, char * breed)
{
this->ID=ID;
this->name = new char [strlen(name)+1];
this->breed = new char[strlen(breed)+1];
strcpy(this->name,name);
strcpy(this->breed,breed);
}
void print()
{
cout<<"name: "<<name;
cout<<" breed: "<<breed;
cout<<" ID: "<<ID<<endl;
}
};

Now we have a class cat and we can create an instance and print its values using the function print().

But I want to be able to print an instance of cat the same way I print an integer, by simply using cout and the << operator. If I were to try doing that all I would get would be some stange number which is the memory address of the cat instance. In order for my printing to work the way I want it to I need to define the behavior of the << operator for my class. I can do this by overloading the << operator like this:

ostream &operator<<(ostream &mycout, const cat &instance)
{
mycout<<"name: "<<instance.name;
mycout<<" breed: "<<instance.breed;
mycout<<" ID: "<<instance.ID<<endl;
return mycout;
}

Just like the + operator, the << operator takes two values, a left hand value and a right hand value. In a command like cout << 17; the left value (or lvalue) is cout which is of type ostream. The right value (or rvalue) is 17 of type int. When I try to print an instance of cat my rvalue will be of type cat. The lvalue will still be of type ostream. The two parameters in the above function are the lvalue and rvalue respectively. Thus, mycout refers to the lvalue and instance refers to the rvalue. The return type will be of type ostream since I want a command like cout<<myCat<<myOtherCat; to work. Let me explain that, the computer will evaluate that line from left to right. It will execute the command cout<<myCat; and return an ostream instance. Then the computer will see that ostream instance followed by <<myOtherCat;. This will then also be able to execute. If we had returned, let's say, an int for example, the computer would not be able to execute someInteger<<myCat<<myOtherCat;. In order to main the fact that the lvalue must always be an ostream , we must return an ostream.

Furthermore, since we cannot run the constructor on the ostream object we must get it my reference (hence the &). If we were to write ostream &operator<<(ostream mycout, const cat &instance) then the operator call would try to create a copy of the ostream intacne cout, something which will not work since the constructor is private (This is how the ostream class was restricted to being a singleton.) For the same reason the retuirn value must be a reference. The rvalue could be passed by value, but I have chosen to pass it also by reference since I can then save a call to the copy constructor. I have made the rvalue const so that I do not accidently try to change it. This operator should have no sideeffects. The const will help insure that.

If I try to run this operator on my class it will fail. That is because the operator<< is trying to access private attributes of the class even though it is not a member of the class. I might think to make the operator a member of the class, but then I would need to call it by having the lvalue be an instance of the class (Remember, only instances of the class can call member function). But in the case I need the lvalue to be of type ostream (i.e. cout). So what how can I grant the operator access to private attributes? There is a special mechanism for doing this, it is called friend. By making a non-member function a friend, I grant it access to private attributes of the class. I will need to add a line like this to my class, usually after the public: declaration.

friend ostream &operator<<(ostream &mycout, const cat &instance);

Now I can write a main that will create a cat instance and print it using the operator<<

int main()
{
cat fefe(17, "feefee", "persian");
cout<<fefe;
return 0;
}

Try using a template for operator overloading for class with primitive data types.

Example overloading the addition and subtraction operators for a simple integer class. Note that while it is preferable to use membership when overloading an operator where the lvalue is the class, one can alternatively use friendship instead.

Another Operator Overloading example

© Nachum Danzig April 2010