Tải bản đầy đủ (.pdf) (10 trang)

A Complete Guide to Programming in C++ part 45 pdf

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (229.18 KB, 10 trang )

USING OVERLOADED OPERATORS

419
ᮀ Calling Operator Functions
The following expressions are valid for the operators in the Euro class.
Example: Euro wholesale(15,30), retail,
profit(7,50), discount(1,75);
retail = wholesale + profit;
// Call: wholesale.operator+( profit)
retail -= discount;
// Call: retail.operator-=( discount)
retail += Euro( 1.49);
// Call: retail.operator+=( Euro(1.49))
These expressions contain only Euro type objects, for which operator functions have
been defined. However, you can also add or subtract int or double types. This is made
possible by the Euro constructors, which create Euro objects from int or double
types. This allows a function that expects a Euro value as argument to process int or
double values.
As the program opposite shows, the statement
Example: retail += 9.49;
is valid. The compiler attempts to locate an operator function that is defined for both the
Euro object and the double type for +=. Since there is no operator function with
these characteristics, the compiler converts the double value to Euro and calls the
existing operator function for euros.
ᮀ Symmetry of Operands
The available constructors also allow you to call the operator functions of + and – with
int or double type arguments.
Example: retail = wholesale + 10; // ok
wholesale = retail - 7.99; // ok
The first statement is equivalent to
retail = wholesale.operator+( Euro(10));


But the following statement is invalid!
Example: retail = 10 + wholesale; // wrong!
Since the operator function was defined as a method, the left operand must be a class
object. Thus, you cannot simply exchange the operands of the operator +. However, if
you want to convert both operands, you will need global definitions for the operator
functions.
420

CHAPTER 19 OVERLOADING OPERATORS
=
()
[]
-
>
Assignment operator
Function call
Subscript operator
Class member access
Operators Meaning
The function call operator () is used to represent operations for objects like function calls. The
overloaded operator -> enables the use of objects in the same way as pointers.

NOTE
// Euro.h
// The class Euro represents a Euro with
// global operator functions implemented for + and
//
#ifndef _EURO_H_
#define _EURO_H_
//

class Euro
{
// Without operator functions for + and
// Otherwise unchanged, specifically with regard to
// the operator functions implemented for += and -=.
};
//
// Global operator functions (inline)
// Addition:
inline Euro operator+( const Euro& e1, const Euro& e2)
{
Euro temp(e1);
temp += e2;
return temp;
}
// Subtraction:
inline Euro operator-( const Euro& e1, const Euro& e2)
{
Euro temp(e1); temp -= e2;
return temp;
}
#endif // _EURO_H_

GLOBAL OPERATOR FUNCTIONS
Operators overloadable by methods only
The operator functions of the following operators have to be methods:
The new Euro class
GLOBAL OPERATOR FUNCTIONS

421

ᮀ Operator Functions: Global or Method?
You can define an operator function as a global function instead of a method. The four
operators listed opposite are the only exceptions.
+= -= *= /= %=
These operators always require a so-called l-value as their left operand, that is, they
require an object with an address in memory.
Global operator functions are generally preferable if one of the following situations
applies:
■ the operator is binary and both operands are symmetrical, e.g. the arithmetic
operators + or *
■ the operator is to be overloaded for another class without changing that class, e.g.
the << operator for the ostream class.
ᮀ Defining Global Operator Functions
The operands for a global operator function are passed as arguments to that function.
The operator function of a unary operator thus possesses a single parameter, whereas the
operator function of a binary operator has two.
The Euro class has been modified to provide a global definition of the operator func-
tions for the operators + and
Example: Euro operator+(const Euro& e1, const Euro& e2);
Both operands are now peers. More specifically, conversion of int or double to Euro
is performed for both operands now. Given a Euro object net, the following expres-
sions are valid and equivalent:
Example: net + 1.20 and 1.20 + net
They cause the following function calls:
operator+( net, 1.20) and
operator+( 1.20, net)
However, a global function cannot access the private members of the class. The func-
tion operator+() shown opposite therefore uses the += operator, whose operator
function is defined as a method.
A global operator function can be declared as a “friend” of the class to allow it access

to the private members of that class.
422

CHAPTER 19 OVERLOADING OPERATORS
// Euro.h
// The class Euro with operator functions
// declared as friend functions.
//
#ifndef _EURO_H_
#define _EURO_H_
//
class Euro
{
private:
long data; // Euros * 100 + Cents
public:
// Constructors and other methods as before.
// Operators -(unary), +=, -= as before.
// Division Euro / double :
Euro operator/( double x) // Division *this/x
{ // = *this * (1/x)
return (*this * (1.0/x));
}
// Global friend functions
friend Euro operator+( const Euro& e1, const Euro& e2);
friend Euro operator-( const Euro& e1, const Euro& e2);
friend Euro operator*( const Euro& e, double x)
{
Euro temp( ((double)e.data/100.0) * x) ;
return temp;

}
friend Euro operator*( double x, const Euro& e)
{
return e * x;
}
};
// Addition:
inline Euro operator+( const Euro& e1, const Euro& e2)
{
Euro temp; temp.data = e1.data + e2.data;
return temp;
}
// Subtraction:
inline Euro operator-( const Euro& e1, const Euro& e2)
{
Euro temp; temp.data = e1.data - e2.data;
return temp;
}
#endif // _EURO_H_

FRIEND FUNCTIONS
Class Euro with friend functions
FRIEND FUNCTIONS

423
ᮀ The Friend Concept
If functions or individual classes are used in conjunction with another class, you may
want to grant them access to the private members of that class. This is made possible
by a friend declaration, which eliminates data encapsulation in certain cases.
Imagine you need to write a global function that accesses the elements of a numerical

array class. If you need to call the access methods of the class each time, and if these
methods perform range checking, the function runtime will increase considerably. How-
ever, special permission to access the private data members of the class can dramatically
improve the function’s response.
ᮀ Declaring Friend Functions
A class can grant any function a special permit for direct access to its private members.
This is achieved by declaring the function as a friend. The friend keyword must pre-
cede the function prototype in the class definition.
Example: class A
{ // . . .
friend void globFunc( A* objPtr);
friend int B::elFunc( const A& objRef);
};
Here the global function globFunc() and the method elFunc() of class B are
declared as friend functions of class A. This allows them direct access to the private
members of class A. Since these functions are not methods of class A, the this pointer is
not available to them. To resolve this issue, you will generally pass the object the func-
tion needs to process as an argument.
It is important to note that the class itself determines who its friends are. If this were
not so, data encapsulation could easily be undermined.
ᮀ Overloading Operators with Friend Functions
The operator functions for + and - in the Euro class are now defined as friend func-
tions, allowing them direct access to the private member data.
In order to compute interest, it is necessary to multiply and divide euros by double
values. Since both the expression Euro*num and num*Euro are possible, friend func-
tions are implemented to perform multiplications. As the example shows, friend func-
tions can also be defined inline in a class.
424

CHAPTER 19 OVERLOADING OPERATORS

// Result.h
// The class Result to represent a measurement
// and the time the measurement was taken.
//
#ifndef _RESULT_
#define _RESULT_
#include "DayTime.h" // Class DayTime
class Result
{
private:
double val;
DayTime time;
public:
// Constructor and access methods
friend class ControlPoint; // All methods of
}; // ControlPoint are friends.
#include Result.h
class ControlPoint
{
private:
string name; // Name of control point
Result measure[100]; // Table with results
// . . .
public:
// Constructor and the other methods
// . . .
// Compute static values of measurement results
// (average, deviation from mean, ).
bool statistic(); // Can access the private
// members of measure[i].

};

FRIEND CLASSES
Class Result
Class ControlPoint
FRIEND CLASSES

425
ᮀ Declaring Friend Classes
In addition to declaring individual friend functions, you can also make entire classes
“friends” of another class. All the methods in this “friendly” class automatically become
friend functions in the class containing the friend declaration.
This technique is useful if a class is used in such close conjunction with another class
that all the methods in that class need access to the private members of the other class.
For example, the class ControlPoint uses objects of the Result class. Calcula-
tions with individual measurements are performed repeatedly. In this case, it makes sense
to declare the ControlPoint class as a friend of the Result class.
Example: class Result
{
// . . .
friend class ControlPoint;
};
It is important to note that the ControlPoint class has no influence over the fact that
it is a friend of the Result class. The Result class itself decides who its friends are and
who has access to its private members.
It does not matter whether a friend declaration occurs in the private or public
section of a class. However, you can regard a friend declaration as an extension of the
public interface. For this reason, it is preferable to place a friend declaration in the
public area of a class.
ᮀ Using Friend Functions and Classes

Using friend functions and friend classes helps you to create efficient programs.
More specifically, you can utilize global friend functions where methods are not suited
to the task in hand. Some common uses are global operator functions declared as friend
functions.
However, extensive use of friend techniques diffuses the concept of data encapsula-
tion. Allowing external functions to manipulate internal data can lead to inconsistency,
especially if a class is modified or extended in a later version. For this reason, you should
take special care when using friend techniques.
426

CHAPTER 19 OVERLOADING OPERATORS
// Array_t.cpp
// A simple class to represent an array
// with range checking.
//
#include <iostream>
#include <cstdlib> // For exit()
using namespace std;
#define MAX 100
class FloatArr
{
private:
float v[MAX]; // The array
public:
float& operator[](int i);
static int MaxIndex(){ return MAX-1; }
};
float& FloatArr::operator[]( int i )
{
if( i < 0 || i >= MAX )

{ cerr << "\nFloatArr: Outside of range!" << endl;
exit(1);
}
return v[i]; // Reference to i-th element.
}
int main()
{
cout << "\n An array with range checking!\n"
<< endl;
FloatArr random; // Create array.
int i; // An index.
// Fill with random euros:
for( i=0; i <= FloatArr::MaxIndex(); ++i)
random[i] = (rand() - RAND_MAX/2) / 100.0F;
cout << "\nEnter indices between 0 and "
<< FloatArr::MaxIndex() << "!"
<< "\n (Quit by entering invalid input)"
<< endl;
while( cout << "\nIndex: " && cin >> i )
cout << i << ". element: " << random[i];
return 0;
}

OVERLOADING SUBSCRIPT OPERATORS
A class representing arrays
OVERLOADING SUBSCRIPT OPERATORS

427
ᮀ Subscript Operator
The subscript operator [] is normally used to access a single array element. It is a binary

operator and thus has two operands. Given an expression such as v[i], the array name
v will always be the left operand, whereas the index i will be the right operand.
The subscript operator for arrays implies background pointer arithmetic, for example,
v[i] is equivalent to *(v+i). Thus, the following restrictions apply to non-overloaded
index operators:
■ an operand must be a pointer—an array name, for example
■ the other operand must be an integral expression.
ᮀ Usage in Classes
These restrictions do not apply if the index operator is overloaded for a class. You should
note, however, that the operator function is always a class method with a parameter for
the right operand. The following therefore applies:
■ the left operand must be a class object
■ the right operand can be any valid type
■ the result type is not defined.
This allows for considerable flexibility. However, your overloading should always reflect
the normal use of arrays. More specifically, the return value should be a reference to an
object.
Since an index can be of any valid type, the possibilities are unlimited. For example,
you could easily define associative arrays, that is, arrays whose elements are referenced by
strings.
ᮀ Notes on the Sample Program
Range checking is not performed when you access the elements of a normal array. An
invalid index can thus lead to abnormal termination of an application program. How-
ever, you can address this issue by defining your own array classes, although this may
impact the speed of your programs.
The opposite page shows a simple array class definition for float values. The sub-
script operator [] has been overloaded to return a reference to the i-th array element.
However, when the array is accessed, range checking is performed to ensure that the
index falls within given boundaries. If an invalid index is found, the program issues an
error message and terminates.

The class FloatArr array has a fixed length. As we will see, variable lengths are pos-
sible using dynamic memory allocation.
428

CHAPTER 19 OVERLOADING OPERATORS
// Euro.h : Class Euro to represent an Euro
//
#ifndef _EURO_H_
#define _EURO_H_
//
class Euro
{ // The class is left unchanged.
// The print() method is now superfluous.
};
//
// Declaration of shift operators:
ostream& operator<<(ostream& os, const Euro& e);
istream& operator>>(istream& is, Euro& e);
#endif // _EURO_H_
// Euro_io.cpp
// Overload the shift operators
// for input/output of Euro type objects.
//
#include "Euro.h"
#include <iostream>
using namespace std;
// Output to stream os.
ostream& operator<<(ostream& os, const Euro& e)
{
os << e.asString() << " Euro"; return os;

}
// Input from stream is.
istream& operator>>(istream& is, Euro& e)
{
cout << "Euro amount (Format x,xx): ";
int euro = 0, cents = 0; char c = 0;
if( !(is >> euro >> c >> cents)) // Input.
return is;
if( (c != ',' && c != '.')
|| cents>=100) // Error?
is.setstate( ios::failbit); // Yes => Set
else // fail bit.
e = Euro( euro, cents); // No => Accept
return is; // value.
}

OVERLOADING SHIFT-OPERATORS FOR I/O
Declaration of the operator functions
Definition of operator functions

×