Operator Overloading And Beyond Computer Science Essay

Published:

Function overloading is based on C++ distinguishing between two functions with the same name as long as the number of parameters or their data types are different. An example often used in this context is the C function that returns the absolute value of a number: abs() when the argument is an int, labs() when it is a long, and fabs() when it is a float. In C++ it is possible to implement this operation by using three different functions with the name abs(), since the compiler is able to select the appropriate one by means of the argument.

Note that the return type of a function cannot be used in overloading, since this element may not be visible at call time. For example:

void fun1(); \\ ILLEGAL prototypes for two functions

int fun1(); \\ with the same name

.

.

cout << fun1(); \\ Compiler cannot resolve which function

Lady using a tablet
Lady using a tablet

Professional

Essay Writers

Lady Using Tablet

Get your grade
or your money back

using our Essay Writing Service!

Essay Writing Service

\\ is referenced in the call

7.2 Operator Overloading

Compile time polymorphism is also achieved by means of operator overloading. In this case a standard C++ operator is given a functionality in addition to its conventional one. Every C++ programmer is familiar with the << and the >> operators used by the cout and cin functions of the iostream library. In this case the < and the > symbols retain their original meanings when used individually. In this same sense we sometimes fail to notice that some arithmetic operators (+, -, *, and /) work on various data types. Thus we use the same symbols for adding, subtracting, multiplying, and dividing integers or reals. In this case the language itself is overloading the operators. What is unique about C++, relative to C, is that it allows the programmer to overload operators. When used judiciously this feature of C++ can make programs easier to use and more rational. When abused, it can cause a debugging nightmare.

7.3 Polymorphism Using Function Overloading

Prior to start with function overloading, we can put forward the concept of polymorphism again. Polymorphism is a means of giving different meanings to the same function name or operator, dependent on context. The appropriate meaning is selected on the basis of the type of data being processed. We have encountered one form of polymorphism when writing expressions of mixed type. Depending on the type of the operands, the division operator on native types might be either an integer division or a floating-point division. Object orientation takes advantage of polymorphism by linking behavior to the object's type. Operators, such as + and <<, have distinct meanings overloaded by operand type. For example, the expression cout << x is by convention expected to display an appropriate representation of x, depending on the type of object x. Overloading of functions gives the same function name different meanings. The name has several interpretations that depend on function selection. This is called ad hoc polymorphism. The remainder of this chapter discusses overloading, especially operator overloading, and conversions of data types. Operators are overloaded and selected based on the signature-matching algorithm. A new meaning is given to them by the overloading of operators. Taking the variables, a and b as an example in the expression a+b, gives us a different meaning depending upon the data type of variables a and b. Overloading the operator + for user-defined types allows them to be used in addition expressions in much the same way native types are used. The expression a + b may have different meanings, for example it could denote addition of integers or floting point constants, concatenation of strings or it may also mean complex number addition, depending upon a number of factors such as, whether the variables were the user-defined ADT my_string, the standard library class complex, or the native type int. Mixed-type expressions are also made possible by defining conversion functions. One principle of OOP is that user-defined types must enjoy the same privileges as native types. Where the C++ standard library adds the complex number type, the programmer expects the convenience of using it without regard to a native/non-native distinction. Operator overloading and user-defined conversions let us use complex numbers in much the same way as we can use int or double.

7.4 Overloading and Signature Matching

Lady using a tablet
Lady using a tablet

Comprehensive

Writing Services

Lady Using Tablet

Plagiarism-free
Always on Time

Marked to Standard

Order Now

Overloaded functions are an important polymorphic mechanism in C++. The overloaded meaning is selected by matching the argument list of the function call to the argument list of the function declaration. When an overloaded function is invoked, the compiler must have a selection algorithm with which to pick the appropriate function. The algorithm that accomplishes this depends on what type conversions are available and is called the signature matching algorithm. A best match must be unique, must be best on at least one argument, and must be as good as any other match on all other arguments. The following list shows the signature matching algorithm for each argument.

Basic Signature Matching Algorithm

Use an exact match if found.

Try standard type promotions.

Try standard type conversions.

Try user-defined conversions.

Use a match to ellipsis if found.

Standard promotions-conversions from float to double and from bool, char, short,

or enum to int-are better than other standard conversions. Standard conversions also

include pointer conversions. An exact match is clearly best. Casts can be used to force such a match. The compiler complains about ambiguous situations. Thus, it is poor programming practice to rely on subtle type distinctions and implicit conversions that obscure the overloaded function.

When in doubt, use explicit conversions to provide an exact match. Let us write an overloaded function greater() and follow our algorithm for various invocations. In this example, the user type rational is available.

// Overloading functions

class rational {

public:

rational(int n = 0) : a(n), q(1) { }

rational(int i, int j) : a(i), q(j) { }

rational(double r) : a(static_cast<long> (r * BIG)),

q(BIG) { }

void print() const { cout << a << " / " << q; }

operator double()

{ return static_cast<double>(a) / q; }

private:

long a, q;

enum { BIG = 100 };

};

inline int greater(int i, int j)

{ return (i > j ? i : j); }

inline double greater(double x, double y)

{ return (x > y ? x : y); }

inline rational greater(rational w, rational z)

{ return (w > z ? w : z); }

int main()

{

int i = 10, j = 5;

float x = 7.0;

double y = 14.5;

rational w(10), z(3.5), zmax;

cout << "\ngreater(" << i << ", " << j << ") = "

<< greater(i, j);

cout << "\ngreater(" << x << ", " << y << ") = "

<< greater(x, y);

cout << "\ngreater(" << i << ", ";

z.print();

cout << ") = "

<< greater(static_cast<rational>(i), z);

zmax = greater(w, z);

cout << "\ngreater(";

w.print();

cout << ", ";

z.print();

cout << ") = ";

zmax.print();

cout << endl;

}

The output from this program is

A variety of conversion rules, both implicit and explicit, are being applied.

greater(10, 5) = 10

greater(7, 14.5) = 14.5

greater(10, 350 / 100) = 10

greater(10 / 1, 350 / 100) = 10 / 1

Inside the code

rational(double r) : a(static_cast<long>(r * BIG),

q(BIG) { }

This constructor converts from double to rational.

operator double()

{ return static_cast<double>(a) / q; }

This member function converts from rational to double. This is only approximately an arithmetically correct conversion.

inline int greater(int i, int j)

{ return (i > j ? i : j); }

inline double greater(double x, double y)

{ return (x > y ? x : y); }

inline rational greater(rational w, rational z)

{ return w > z ? w : z); }

Three distinct functions are overloaded. The most interesting has rational type for its argument list variables and its return type. The conversion member function operator double() is required to evaluate w > z. Later, we shall show how to overload operator>() to take rational types directly.

cout << "\ngreater(" << i << ", " << j << ") = "

Lady using a tablet
Lady using a tablet

This Essay is

a Student's Work

Lady Using Tablet

This essay has been submitted by a student. This is not an example of the work written by our professional essay writers.

Examples of our work

<< greater(i, j);

cout << "\ngreater(" << x << ", " << y << ") = "

<< greater(x, y);

The first statement selects the first definition of greater() because of the exact-match rule. The second statement selects the second definition because of a standard widening promotion float to double where variable x is widened to double.

7.5 Friend Functions

The term friend is a keyword which is used to describe a function and its work is to allow a function which is not a member of the class to access the hidden members of the class and provides a method of escaping the data-hiding restrictions of C++. However, we must have a good reason for escaping these restrictions, as they are important to reliable programming. One reason for using friend functions is that some functions need privileged access to

more than one class. A second reason is that friend functions pass all of their arguments through the argument list, and each argument value is subject to assignment compatible conversions. Conversions apply to a class variable passed explicitly and are especially useful in cases of operator overloading,

A friend function is to be declared within the class declaration to which it has been assigned as a friend. The keyword, friend has to be written before the function and it can appear in any portion of the class without changing its meaning. The most suitable way is to declare the friend function in the part of the class which is public. Since there is no issue of access to the friend function the declarations can be ctually treated as public. A friend function to one class could be a private member of another class, and hence it may not be public. Friend function of one class may be the member function of another class. In such a scenario this case, they are written within the friend's class, by using an operator known as scope resolution operator to sanctify its function name. In order to specify that all member functions of one particular class are friend functions of another class, the syntax should be: friend class class-name.

The following declarations illustrate the syntax.

void alice()

{

// use some private stuff from tweedledee

·····

cout << "Have some more tea.\n";

}

_ << greater(static_cast<rational>(i), z);

The third definition of greater() is selected because of the best match rule. The explicit conversion of i to a rational is necessary to avoid ambiguity. Then the rational is implicitly converted to double.

zmax = greater(w, z);

This is an exact match for the third definition.

class tweedledee {

·····

friend void alice(); // friend function

int cheshire(); // member function

·····

};

class tweedledum {

·····

// friend member function

friend int tweedledee::cheshire();

·····

};

class tweedledumber {

·····

// all member functions of tweedledee have access

friend class tweedledee;

·····

};

The global function alice() has access to all members of tweedledee. The member function tweedledee::cheshire() is given access to all the members of tweedledum. All member functions of tweedledee are given access to all the members of tweedledumber.

7.6 Overload Resolution

The process of selecting a function to call for a given call expression is known as overload resolution. For example:

void display_num(int); // (1)

void display_num(double); // (2)

int main()

{

display_num(399); // matches (1) better than (2)

display_num(3.99); // matches (2) better than (1)

}

In this example, the function name display_num() is said to be overloaded. When this name is used in a call, a C++ compiler must therefore distinguish between the various candidates using additional information; mostly, this information is the types of the call arguments. In our example it makes intuitive sense to call the int version when the function is called with an integer argument and the double version when a floating-point argument is provided. The formal process that attempts to model this intuitive choice is the overload resolution process.

The general ideas behind the rules that guide overload resolution are simple enough, but the details have become quite complex during the C++ standardization process. This complexity was driven mostly by the desire to support various real-world examples that intuitively (to a human) seem to have an "obviously best match," but when trying to formalize this intuition, various subtleties arose.