C++/c++ operator overloading
Expert: Ralph McArdell - 8/26/2007
Questionhi..
i want to know how the operator over loading functions are invoked..for example
how s1+s1(both are objects) will invoke
class_name operator(class_name object_1)
if my question is not clear please give me an example and explanation for operater verloading....
with thanks.. sathish
AnswerI do not know if you really intended to say:
s1+s1
in your question, but maybe instead meant:
s1+s2
as you go onto say both are objects implying two objects and not just one are involved. I shall assume the latter.
The compiler sees that s1 and s2 are objects of some type(s) for which it has no built-in addition operation, so it goes looking in what it knows about these types and user defined overloaded operator functions to see if it can find an overloaded operator function it can call to perform the addition operation for the types of the objects s1 and s2.
One place it will look is the class definition for the type of s1 to see if there is an operator+ member function defined that takes an object of s2's type as a parameter:
class S1Type
{
public:
// ...
S1Type operator+( S2Type const & rhs ) const
{
// ...
}
// ...
};
Here I have assumed the types of s1 and s2 are different. Notice that I call the S2Type parameter passed to the S1Type operator+ function rhs - short hand for right hand side. This is because this parameter appears on the right hand side of the binary (meaning it takes 2 operands) + operator. This means that s1+s2 would work but s2+s1 would not unless S2Type defined an operator+ function that took an S1Type as a parameter. As I assume s2 is a class type that may be quite large I pass it by constant reference rather than by value. Constant reference is used as the operation should not need to modify the operands. This is also why I declare the operator+ member function const - the s1 (this) object should not be modified either.
So effectively when the compiler sees:
s1 + s2
It looks for a suitable operator+ function. If it finds one and it is a member function of s1's type (S1Type in my examples) then it is as if it replaces the (sub-) expression:
s1 + s2
with a call to S1Type::operator+(S2Type const &):
s1.operator+(s2)
The result in this case is an object of s1's type, which has the result of adding s2 to it. However it could be an object of s2's type (S2Type in my examples) or some other type. Note that this follows the usual meaning of a binary + operation: neither of the operands is modified, rather they produce a third object as the result.
The other type of overloaded operator function the compiler looks for is a non-member overloaded operator+ function:
class S1Type
{
public:
// ...
};
class S2Type
{
public:
// ...
};
S1Type operator+( S1Type const & lhs, S2Type const & rhs )
{
// ...
}
Here the function takes two parameters - the left hand side value and the right hand side value. If this were the operator+ function chosen for our (sub-) expression then it would be as if the compiler replaced:
s1 + s2
with a call to the non-member operator+ function:
operator+(s1, s2)
Non-member overloaded operator functions have to be used in some cases as the left hand side is not of an appropriate type. An example are the stream insertion and extraction operators (operator<< and operator>>). In these cases to be a member function would mean the stream classes would need to be updated with new operator functions for every type that wished to support stream insertion and extraction syntax:
std::cout << s1;
std::cin >> s2;
etc.
as it is the stream objects that are on the left hand side. This is somewhat impractical even if they were not standard C++library types. As they are standard library types they definitely should not be modified by users of the library (i.e. every day C++ developers like you and me) and indeed cannot be according to the standard. So instead we write operator<< and operator>> functions for our own types as non-member functions.
Of course if in its search for an appropriate operator function to handle the addition the compiler finds no suitable functions or more than one of equal viability then the program is in error.
Finally I should point out that the compiler has to search in various scopes for non-member overloaded operator functions and for classes with member functions. In fact this is a general rule for any non-member functions. It is only a problem for member functions of overloaded operators as direct member function calls require an object of the class type or the (qualified) class name for static member functions, in which case the compiler has effectively already been told where to look for the class definition!
So not only does the compiler search the current scopes (namespaces) where the call occurs for matching function overloads (operator functions or otherwise), as is usual and expected, it _also_ looks in the namespaces where the types of any arguments are defined.
This is because it is likely if you define a class C in some namespace N then you would declare any non member functions related to class C - such as non member overloaded operator functions for class C objects - in the same namespace.
Hence when using class N::C objects, say from some other namespace X and performing operations on them the compiler would fail to locate the non-member functions relating to class C if it only looked in namespace X and outwards (e.g. the global namespace).
Likewise if when looking for candidate overloaded operator functions it only looked in the current scope and outwards (e.g. namespace X and the global namespace) it could miss class member overloaded operator functions of the types of the parameters, as it would miss the definition for the types (e.g. the definitions of class C in namespace N).
The compiler therefore has to look also in the namespaces of the parameters (or operands for overloaded operators) (e.g. namespace N and namespace std). This is shown below:
namespace N
{
class C
{
// ...
public:
C operator+( C const & rhs ) const
// ...
};
//---------------------------------------------------------------------------
// Output stream insertion operator for class C.
//
// Allows us to write:
//
// C c;
// std::cout << c;
//
std::ostream & operator<<( std::ostream & out, C const & rhs )
{
// ...
return out;
}
}
namespace X
{
//---------------------------------------------------------------------------
// Function using class C, adding two N:C objects
// and displaying results on std::cout:
//
void function_using_class_C
{
N::C c1(2,3); // Need to qualify C with its containing namespace N
N::C c2(4,5);
// If the compiler did not also look in the namespaces of the types
// of the parameter operands std::cout (in std) and c1 and c2 (in N)
// then std::ostream & N::operator<<( std::ostream &, N::C const & )
// and N::C N::C::operator+( N::C const & ) could not be found and the
// following line would raise one or more compiler errors.
std::cout << c1 << " + " << c2 " = " << c1 + c2 << '\n';
}
}
I hope this answers your query. If it does not, or you are unclear on anything in my answer then please do post a follow-up or new question.