Example: Euro wholesale15,30, retail, profit7,50, discount1,75; retail = wholesale + profit; // Call: wholesale.operator+ profit retail -= discount; // Call: retail.operator-= discount r
Trang 1䊐 Calling Operator Functions
The following expressions are valid for the operators in the Euroclass
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 intordoubletypes 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 intor
doublevalues
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
intordoubletype 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
Trang 2420 C H A P T E R 1 9 O V E R L O A D I N G O P E R A T O R S
= () []
->
Assignment operator
Function call
Subscript operator
Class member access
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;
}
■ GLOBAL OPERATOR FUNCTIONS Operators overloadable by methods only
The operator functions of the following operators have to be methods:
The new Euro class
Trang 3䊐 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 ostreamclass
䊐 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.
TheEuroclass 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 intordoubletoEuro
is performed for both operands now Given a Euroobject 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
Trang 4422 C H A P T E R 1 9 O V E R L O A D I N G O P E R A T O R S
// 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
{ // = *this * (1/x) return (*this * (1.0/x));
}
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
Trang 5䊐 The Friend Concept
If functions or individual classes are used in conjunction with another class, you may want to grant them access to the privatemembers 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 friendkeyword 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 friendfunctions 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 thispointer 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 Euroclass 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*numandnum*Euroare possible, friend func-tions are implemented to perform multiplicafunc-tions As the example shows, friend func-tions can also be defined inlinein a class
Trang 6424 C H A P T E R 1 9 O V E R L O A D I N G O P E R A T O R S
// 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
}; // 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
Trang 7䊐 Declaring Friend Classes
In addition to declaring individual friendfunctions, you can also make entire classes
“friends” of another class All the methods in this “friendly” class automatically become
friendfunctions in the class containing the frienddeclaration
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 ControlPointclass as a friend of the Resultclass
Example: class Result
{
// friend class ControlPoint;
};
It is important to note that the ControlPointclass has no influence over the fact that
it is a friend of the Resultclass The Resultclass itself decides who its friends are and who has access to its private members
It does not matter whether a frienddeclaration occurs in the privateorpublic
section of a class However, you can regard a frienddeclaration as an extension of the public interface For this reason, it is preferable to place a frienddeclaration in the
publicarea 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 friendfunctions 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 friendtechniques 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 friendtechniques
Trang 8426 C H A P T E R 1 9 O V E R L O A D I N G O P E R A T O R S
// 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;
int i; // An index
// Fill with random euros: for( i=0; i <= FloatArr::MaxIndex(); ++i)
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
Trang 9䊐 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
vwill always be the left operand, whereas the index iwill 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 floatvalues 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 FloatArrarray has a fixed length As we will see, variable lengths are pos-sible using dynamic memory allocation
Trang 10428 C H A P T E R 1 9 O V E R L O A D I N G O P E R A T O R S
// 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