Overview of C++ Overloading
Overloading occurs when the same operator or
function name is used with different signatures
Both operators and functions can be overloaded
Different definitions must be distinguished by their
signatures (otherwise which to call is ambiguous)
Reminder: signature is the operator/function name and the
ordered list of its argument types
E.g., add(int,long) and add(long,int) have different
signatures
E.g., add(const Base &) and add(const Derived &)
have different signatures, even if Derived is-a Base
Most specific match is used to select which one to call
CSE 332: C++ Overloading
Overloading vs. Overriding
Overriding a base class member function is
similar to overloading a function or operator
But for overriding, definitions are distinguished by
their scopes rather than by their signatures
C++ can distinguish method definitions
according to either static or dynamic type
Depends on whether a method is virtual or not
Depends on whether called via a reference or
pointer vs. directly on an object
Depends on whether the call states the scope
explicitly (e.g., Foo::baz();)
CSE 332: C++ Overloading
Function Overloading
class A {
public:
int add(int i, int j);
Calls to overloaded functions and
operators are resolved by
Finding all possible matches based
on passed arguments
// not allowed, would be
// ambiguous with above:
// long add(int m, int n);
May involve type promotion
May involve instantiating templates
Finding the best match among
those possible matches
// Ok, different signature
long add(long m, long n);
};
Signature does not include the
return type
int
main (int argc, char **argv) {
int a = 7;
int b = 8;
int c = add(a, b);
return 0;
}
CSE 332: C++ Overloading
Which might not help even if it did,
i.e., calls may ignore result
So, overloading cant be resolved by
return type alone
Compiler generates an error if the
call cant be resolved
Operator Overloading
class A {
friend ostream &operator<<
(ostream &, const A &);
private:
int m_;
};
Similar to function
overloading
ostream &operator<<
(ostream &out, const A &a) {
out << "A::m_ = " << a.m_;
return out;
}
int main () {
A a;
cout << a << endl;
return 0;
}
CSE 332: C++ Overloading
Resolved by signature
Best match is used
But the list of operators and
the arity of each is fixed
Cant invent operators (e.g.,
like ** for exponentiation )
Must use same number of
arguments as for built-in types
(except for operator())
Some operators are off limits
:: (scope) ?: (conditional)
.* (member dereference)
. (member) sizeof
typeid (RTTI)
Operator Symmetry, Precedence
class Complex {
public:
// Constructor
Complex (double r, double i);
friend Complex operator*
(const Complex &, const Complex &);
// but not friend Complex operator^
// (const Complex &, const Complex &);
private:
int real_;
int imaginary_;
};
// multiplication works just fine
Complex operator* (const Complex &,
const Complex &);
// exponentiation operator unworkable
// Complex operator^ (const Complex &,
//
const Complex &);
CSE 332: C++ Overloading
Make arithmetic
operators symmetric
As non-member friends
Return result by value
Dont mix base and
derived types in their
parameter lists
Operators for userdefined types obey the
same precedence rules
as for built-in types
Can lead to some
unexpected mistakes
E.g., if uncommented
exponentiation for
a + b * c ^ 2
Member vs. Non-Member Overloading
// declarations in .h file
class A {
public:
friend bool operator<
(const A &, const A &);
A operator++(); // prefix
A operator++(int); // postfix
private:
int m_;
};
bool operator==(const A &lhs,
const A &rhs);
// definitions in .cpp file
bool operator==(const A &lhs,
const A &rhs)
{return lhs.m_ == rhs.m_;}
A A::operator++() // prefix
{++m_; return *this;}
Remember a this pointer is passed
to any non-static member function
Object doesnt appear in parameters
For non-member functions and
operators all parameters are listed
The rules about operator arity are
obeyed in code on left
Operator == is binary
Prefix/postfix ++ are unary, parameter
for postfix distinguishes its signature
Must declare and define [] and ==
and -> and () as member operators
Non-member operators are needed
when working with classes you wrote
and classes you didnt write
E.g., ostream << and istream >>
Non-member operators are also
useful to preserve symmetry
A A::operator++(int) // postfix
{A ret(*this); ++*this; return ret;}
CSE 332: C++ Overloading
E.g., for arithmetic/relational operators
May help to avoid unexpected type
conversions, especially with an
inheritance hierarchy
Summary: Tips on Overloading
Use virtual overriding when you want to substitute
different subtypes polymorphically
E.g., move() in derived and base classes
Use overloading when you want to provide related
interfaces to similar abstractions
E.g., migrate(Bird &) vs. migrate(Elephant &)
Make[] -> () = *= ++ -- etc. members
Make << >> + * - / == < etc. non-members
Use different names when the abstractions differ
E.g., fly() versus walk()
CSE 332: C++ Overloading