Upload
darius
View
63
Download
0
Embed Size (px)
DESCRIPTION
Topics on Ad Hoc Polymorphism. Class-Defined Conversions Overloading and Function Selection Friend Functions Unary Operator Overloading Binary Operator Overloading Overloading Assignment & Subscript Operators Overloading Operator () for Indexing Overloading new and delete - PowerPoint PPT Presentation
Citation preview
1
Topics on Ad Hoc Polymorphism
Class-Defined Conversions Overloading and Function Selection Friend Functions Unary Operator Overloading Binary Operator Overloading Overloading Assignment & Subscript Operators Overloading Operator () for Indexing Overloading new and delete More Signature-Matching Polynomial:Type And Language Expectations
2
General Polymorphism
Polymorphism is a means of giving different meanings to the same message
The meanings are dependent on the type of data being processed
Go getme a ball
3
Type Conversions
Conversion is the explicit or implicit change of value between types
Conversions provide a form of polymorphism
Float Int
4
Ad Hoc Polymorphism
Overloading functions gives the same function name different meanings: ad hoc polymorphism
Name has different interpretations that depend on function selection based on signature-matching algorithm for C++
print (the int) 3
print (the matrix) 2.2 3.3 5.1 9.3
71.3 6.0 9.9 12.55
print (the shape)
5
Overloading and OOP
The LOOK and FEEL principle of OOP is that user-defined types must enjoy the same privileges as native types
Client expects convenience without regard to native/nonnative distinction
Supplier needs to achieve ease of use for ADTs
Native types in the kernel language can be mixed in expressions because it is convenient and would otherwise be burdensome to designate conventionally expected conversions
6
New Cast Notation
A functional notation is equivalent to a casttype-name (expression)
The type must be expressible as an identifier
7
Equivalent Expressions and Casting
x = float(i); //C++ functional notationx = (float) i; //equivalent to abovep = (int*) q; //legal castp = int*(q); //illegaltypedef int* int_ptr; //equivalentp = int_ptr(q);
Functional notation is the preferred style
8
Class-defined Conversions
Explicit type conversion of an expression is necessary when either the implicit conversion is not desired or the expression is not legal otherwise
To achieve the integration of ADTs and native types there are two mechanisms for having a member function provide an explicit conversion
conversion constructors class-name::operator type ( )
complex::complex(double) complex::operator double()
double complex complex double
9
Constructors and Type Conversion
A constructor of one argument is de facto a type conversion from the argument's type to the constructor's class type
string::string(const char* p) { len = strlen(p); s = new char[len + 1]; strcpy(s, p); }
This is automatically a type transfer from char* to string and it is available both explicitly and implicitly
10
The Conversion Operator
General form for conversion member function operator type() { . . . }
A conversion function must be a non-static member function without a return type and with an empty argument list
These conversions occur implicitlyin assignment expressions
in arguments to functions
in values returned from functions
11
Conversion Operator to Native char*
string::operator char*(){ char* q = new char[len + 1]; strcpy(q, s); return (q);}
This defines a special conversion function inside the string class to convert from a string to the native type char *
This form doesn't provide access to the underlying string
12
Explicit & Implicit String Conversions
string s;char* logo = "Geometrics Inc";
//perform explicit conversion then assignments = string(logo);
//implicit invocation of conversions = logo;
These are conversions from an already defined type to a user-defined type
It is not possible for the user to add a constructor to a native type such as int or double
13
A conversion member function of the form A::operator B() and a constructor of the form B::B(const A&) both provide conversions from type A objects to type B objects
Having both can result in ambiguity errors
A B
A B
Ambiguity and Conversions
14
Overloading print()
print(int)
print(int, int)
print(foo_bar)
15
Overloaded Functions in C++
Overloaded functions are an important 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
16
Function Overload Selection Algorithm
1. Use an exact match if found
2. Try standard type promotions
3. Try standard type conversions
4. Try user-defined conversions
5. Use a match to ellipsis if found
17
Best Match and Ambiguities
A best match must be unique and it must be best on at least one argument and as good on all other arguments as any other match
Ambiguity of more than 1 possible match is illegal
Default arguments can lead to ambiguities
18
Declarations int i; double w; complex z; char c, *p; void f(int);f(i); exact matchf(c); standard promotionf(w); standard conversionf(z); user-defined promotionf(p); illegal
Function Selection Algorithm In Use
19
Function Overloading (1 of 5)
// Title: greater#include <iostream.h>#include <math.h> //for sqrtclass complex {public: complex(double r): real(r), imag(0.0) { } void assign(double r, double i) {real = r; imag = i; } void print() {cout<< real<< " + "<< imag << "i ";} operator double() {return (sqrt(real*real+imag*imag));}private: double real, imag;};
20
Function Overloading (2 of 5)
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 complex greater(complex w,complex z) { return ( w > z ? w : z); }
21
Function Overloading (3 of 5)
main(){ int i = 10, j = 5; float x = 7.0; double y = 14.5; complex w(0), z(0), zmax(0); w.assign(x, y); z.assign(i, j); cout << "compare " << i << " and " << j << " greater is " << greater(i, j) << endl; cout << "compare " << x << " and " << y << " greater is " << greater(x, y) << endl; cout << "compare " << y << " and " ;
22
Function Overloading (4 of 5)
z.print(); cout << " greater is " << greater(y, double(z)) << endl; zmax = greater(w, z); cout << "compare "; w.print(); cout << " and "; z.print(); cout << " greater is "; zmax.print(); cout << endl;}
23
Function Overloading (5 of 5)
The output from this program is
compare 10 and 5 greater is 10compare 7 and 14.5 greater is 14.5compare 14.5 and 10 + 5i greater is 14.5compare 7 + 14.5i and 10 + 5i greater is 7 + 14.5i
24
Comments on the greater Program
Three distinct functions are overloaded
The most interesting has complex type for its argument list variables and its return type
The conversion member function operator double is required to evaluate w > z
The complex variables w and z are converted to double
Silently what happens is
double(w) > double(z) ? w : z;
25
Friend Functions
The keyword friend is a function specifier which gives a nonmember function access to the hidden members of the class
Its use is a method of escaping the data-hiding restrictions of C++
There must be a good reason for escaping these restrictions as they are important to reliable programming
26
Letting Non-members in
I’m a friend, so let me in without a membership card, please.
Show membershipcard to get in
OK, come in.
27
Reasons for Using Friend Functions
Friend access follows certain understood situations that would othrwise be inconvenient
Operator overloading of binary operators
Some functions need access where the first argument is not properly a class or is a class argument whose source cannot be modified with additional members
Overriding efficiency concerns
Special relationship between classes
28
Using Friend Functions
A friend function declaration must appear inside the class declaration to which it is a friend
The function is prefaced by the keyword friend and can appear in private or public part
A member function of one class can be a friend function of another class
The member function is declared in the friend's class using the scope resolution operator
If all member functions of one class are friend functions of a second class, then use friend class class-name
29
Access via friends
class foo_bar { friend ostream& operator<< (ostream &, foobar &); . . . };
Because << is tied to ostream class it is not readily modifiable there
As a consequence we need operator << to be either a friend or an ordinary function
The relationship to I/O frequently needs appropriate and efficient access to state
30
Assignment Compatible Conversion
class complex { . . . friend complex operator+(complex, complex); . . .};
1 + w; //with member function this failsw + 1; //this is OK either way
Symmetry of application of assignment compatible conversion rules for arguments is desirable
31
Separating Components into Classes
class tree;class node { . . . friend class tree; . . .};
A special relationship holds between whole and part thatfavors separating thesecomponents
32
Sub-component Access
friend vect mpy(const vect&, const matrix&);
Need efficient sub-component access to implement a basic operation
More generally, classes must interact intimately
33
Using Friends
Multiply vector by matrix could be written efficiently if it had access to both classes
It would be a friend function of both classes
Safe access is provided with member functions vect::element() and matrix::element()
We could write a function using this access that would multiply without requiring friend status, but the price in functional call overhead and array bounds checking would make such a matrix multiply unnecessarily inefficient
34
Using Friends (1 of 3)
// Title: matrix_v
class matrix; //forward referenceclass vect {public: friend vect mpy(const vect& v, const matrix& m); . . .private: int* p; int size;};
35
Using Friends (2 of 3)
class matrix { //stores integer elementspublic: friend vect mpy(const vect& v, const matrix& m); . . .private: int** base; int row_size, column_size;};
36
Using Friends (3 of 3)
vect mpy(const vect& v, const matrix& m){ if (v.size != m.row_size) { //incorrect sizes cerr << "multiply failed—sizes bad" << v.size << " & " << m.row_size; exit(1); } vect ans(m.column_size); //use privileged int i, j; //access to p for (i = 0; i <= m.ub2(); ++i) { ans.p[i] = 0; for (j = 0; j <= m.ub1(); ++j) ans.p[i] += v.p[j] * m.base[j][i]; } return (ans);}
37
Controversy of Using Friend Functions
A neat, orderly design principle is that only member functions should have access to the hidden implementation of the ADT
The friend function straddles this boundary since it has access to private members but is not itself a member function
The friend function can be used to provide quick fixes to code that needs access to the implementation details of a class
The mechanism can be abused easily
38
46
Notational Convenience & Operators
Just as a function name, such as print, can begiven a variety of meanings that depend on itsarguments, so can an operator, such as +, begiven additional meanings
Overloading operators allows ADTs to use C++expression syntax
It is an important notational convenience and inmany instances leads to shorter, more readableprograms
38
39
Operator Overloading (1 of 2)
47
Operator Overloading (1 of 2)
Keyword operator used to overload C++operators
Associativity & precedence remain the same Operator overloading with other than member
functionsarguments are passed as parameters
Unary operator member function argumentsimplicitly passed class variableempty argument list
Binary operator member function argumentsimplicitly passed class variablelone argument list parameter
39
4040
48
Operator Overloading (2 of 2)
Available operators can be overloaded:arithmetic logicalcomparison equalityassignment bitsubscript [] function call ()class pointer -> member pointer ->*new deleteautoincrement ++ autodecrement --
These operators cannot be overloaded:member . member object selector .*sizeof conditional expression ?:scope resolution ::
40
4141
49
Using a Clock
Convert minutes to hours and seconds
41
42
Unary Operator Overloading
Overload unary operators, such as !, ++, ~, and []
For this purpose we develop the class clock, which can be used to store time as days, hours, minutes, and seconds
4343
50
Overloading clock (1 of 7)
class clock {private: unsigned long int tot_secs, secs, mins, hours, days;public: clock(unsigned long int i);//constructor and conversion void print(); //formatted printout void tick(); //add one second clock operator++() { tick(); return (*this); } clock operator—(const clock& c);
43
44
51
Overloading clock (2 of 7)
friend clock operator*(unsigned long int m, const clock& c); friend clock operator*(const clock& c, unsigned long int m); friend clock operator+(const clock& c1, const clock& c2);};
44
4545
52
Overloading clock (3 of 7)
void clock::print(){ cout <<days <<" d :" <<hours << " h :" <<mins <<" m :" <<secs << " s\n";}
inline clock::clock(unsigned long int i){ tot_secs = i; secs = tot_secs % 60; mins = (tot_secs / 60) % 60; hours = (tot_secs / 3600) % 24; days = tot_secs / 86400;}
45
4646
53
Overloading clock (4 of 7)
void clock::tick(){ clock temp = clock(++tot_secs); secs = temp.secs; mins = temp.mins; hours = temp.hours; days = temp.days;}
void clock::reset(clock& c){ tot_secs = c.tot_secs; secs = c.secs; mins = c.mins; hours = c.hours; days = c.days;}
46
47
47 54
Overloading clock (5 of 7)
clock operator*(unsigned long int m, const clock& c){ return (m * c.tot_secs);}
//allow * to work with clock*long or long*clock
clock operator*(const clock& c, unsigned long m){ return (m * c.tot_secs); //return m * c}
47
48
48 55
Overloading clock (6 of 7)
//binary +clock operator+(const clock& c1, const clock& c2){ return (c1.tot_secs + c2.tot_secs);}
//binary -clock clock::operator—(const clock& c){ return( tot_secs — c.tot_secs);}
48
49
56
Overloading clock (7 of 7)
main(){ clock t1(59), t2(172799); cout << "initial times are\n"; t1.print(); t2.print(); ++t1; ++t2; cout << "after one second times are\n"; t1.print(); t2.print(); t2 = t1 + t2 - t1 - t1; t1.print(); t2.print(); t1 = 3 * t2; t1.print(); t2.print();}
49
50
Comments on the clock Program
The constructor performs the usual conversions from tot_secs to days, hours, minutes, and seconds and acts as a conversion function that properly updates the time
The member function tick constructs clock temp, which adds one second to the total time
51
Unary Operator Overloading for Clock
clock operator++() { tick(); return (*this); }
This class overloads the prefix increment operator which updates the implicit clock variable and returns the updated value
The overloaded operator is a member function and can be invoked on its implicit single argument
52
Member v. Non-Member Overloading
Overload prefix ++ using a friend function with call-by-reference:
clock operator++(clock& cl) { cl.tick(); return (cl); }
The decision to choose between a non-member and a member function typically depends on whether implicit conversion operations are available and desirable
Explicit argument passing, as in non-member functions, allows the argument to be automatically coerced
53
Binary Operator Overloading
When a binary operator is overloaded using a member function, it has as its first argument the implicitly passed class variable and as its second argument the lone argument list parameter
A friend function or an ordinary function has both arguments specified in the parameter list
An ordinary function cannot access private members
54
Binary Addition Friend Function
friend clock operator+(const clock& c1,const clock& c2)
Both arguments are candidates for assignment conversions
clock operator+(const clock& c1, const clock& c2) { return (c1.tot_secs + c2.tot_secs); }
Both arguments are specified explicitly
The constructor converts unsigned long int expression into a clock value
55
Binary Subtraction Member Function
clock operator-(const clock& c);
In the - operation, there is an implicit first argument which takes some getting used to and which can cause asymmetric behavior for binary operators
clock clock::operator-(const clock& c){ return (tot_secs - c.tot_secs);}
56
Binary Multiplication Friend Function
The multiplication operation is a binary operation with one argument an unsigned long int and the second argument a clock variable
clock operator*(unsigned long int m, const clock& c){ return(m * c.tot_secs);}
Implementation forces the multiplication to have a fixed ordering that is type-dependent
Common practice to write a second overloaded function to allow either ordering of operands
57
Using the Binary clock Operations
int i = 5;clock c(900), d(800);c + i //legal: i converted to a clocki + c //legal: i converted to a clockc — i //legal: i converted to a clockc.operator—(i) //function call notationi — c //illegal: i is not a clocki.operator—(c) //illegal: function call notationi * c //legal
As seen clearly in the use of function call notation, the variable i is not a clock and does not "understand" the meaning of minus
58
Lvalues and Rvalues
A = B
c[i] = 5 + D[j]
59
Overloading Assignment & Subscript
C++ has reference declarations, and such type modifiers produce lvalues
lvalue stands for location value
On the right side of an assignment expression, an lvalue is automatically dereferenced
On the left side of an assignment expression, an lvalue specifies where a value is to be stored
Subscripting uses of these properties of lvalues
For ADTs define expressions such as subscript & assignment, unless defaults are satisfactory
60
Re-implementing the Class vect
The reimplemented vect class has several improvements to make it safer and more useful
A constructor that converts an ordinary integer array to a safe array allows us to develop code using safe arrays and later convert the same code to run efficiently using ordinary arrays
The public data member ub is changed to a member function preventing a user from introducing a program error by modifying ub
The subscript operator [] is overloaded and replaces the member function element
61
Making a Safe Array
5715
There isn’t a6th one to change!Let’s
change the6th vectorelement.
62
Safe Array with Subscripting (1 of 6)
class vect {public: vect(); //create a size 10 array vect(int n); //create a size n array vect(const vect& v); //init by vect vect(const int a[], int n); //init by array ~vect() { delete [] p}; int ub() const { return (size — 1); }//upper bound int& operator[](int i); //range checked element vect& operator=(const vect& v); //overload assign vect operator+(const vect& v); //overload addprivate: int* p; //base pointer int size; //number of elements};
63
Safe Array with Subscripting (2 of 6)
//Add two further constructors//Constructor 1 converts a normal array
vect::vect(const int a[], int n){ asert (n > 0); size = n; p = new int[size]; assert (p); //throw xalloc; for (int i = 0; i < size; ++i) p[i] = a[i];}
64
Safe Array with Subscripting (3 of 6)
//Constructor 2 is copy constructor
vect::vect(const vect& v){ size = v.size; p = new int[size]; assert (p); for (int i = 0; i < size; ++i) p[i] = v.p[i];}
65
Safe Array with Subscripting (4 of 6)
The overloaded subscript operator takes an integer argument and tests value is within range
If so, it uses it to return the lvalue of the indexed element
int& vect::operator[](int i){ if (i < 0 || i > (size — 1)) { cerr << "illegal vect index: " << i << endl; exit(1); //throw boundserr(); } //later will use exceptions return (p[i]);}
66
Safe Array with Subscripting (5 of 6)
vect& vect::operator=(const vect& v){ int s = (size < v.size) ? size : v.size; if (v.size != size) cerr << "copying different size " << "arrays " << size << " and " << v.size << endl; for (int i = 0; i < s; ++i) p[i] = v.p[i]; return (*this);}
67
Safe Array with Subscripting (6 of 6)
vect vect::operator+(const vect& v){ int s = (size < v.size) ? size : v.size; vect sum(s); //vect * sumptr = new vect(s); if (v.size != size) cerr << "adding different size " << "arrays " << size << " and " << v.size << endl; for (int i = 0; i < s; ++i) sum.p[i] = p[i] + v.p[i]; return (sum); //return (*sumptr);}
68
Comments on Subscript Function
An overloaded subscript operator has a return type and a single argument
It must be a non-static member function
It is good style to maintain the consistency between a user-defined meaning of the subscripting operator [] and standard usage
A most common function prototype is class name& operator[](integral type);
A reference value is returned in such functions that can be used on either side of an assignment expression
69
Shallow Copy Semantics and vect
Provide your own assignment operator anytime pointers are part of the ADT implementation
When assignment is not overloaded its default is memberwise assignment of value
This is shallow copy semantics and can be incorrect
Class provider makes sure that default semantics are correct
If not, as is the case here with vect, the class provider must overload the construct with the correct semantics, or alternatively overload the assignment operator with error-signalling behavior
70
Comments on Assignment Function
The explicit argument v.p[] is the right side of the assignment
The implicit argument, as represented by p[], is the left side of the assignment
The self-referential pointer is dereferenced and passed back as the value of the expression
Allows multiple assignment with right-to-left associativity
Function could have been written to return void, but then it would not allow multiple assignment
71
Using vect Addition and Assignment
Meaningful with the extended class vect:
a = b; //a, b are type vecta = b = c; //a, b, c are type vecta = vect(data, DSIZE); //convert array data[DSIZE]a = b + a; //assignment and additiona = b + (c = a) + d; //complicated expression
The class vect is a full-fledged ADT
It behaves and appears in client code much as any built-in type behaves and appears
72
Overloading Operator () For Indexing
Dynamically allocated two-dimensional arrays can be designed with the function call operator overloaded to provide element selection
The matrix is allocated as a column of pointers that are base addresses for a row of elements
assert.h provides a macro that dynamically tests a condition and reports on failure
The function call operator () is overloadable as a non-static member function
Provides an iterator operation or an operation requiring multiple indices
73
Overloading () (1 of 5)
// Title: matrix#include <assert.h>
class matrix {public: matrix(int c, int r); ~matrix(); double& operator()(int i, int j) const { return (p[i][j]); } matrix& operator=(const matrix& m); matrix& operator+=(const matrix& m);private: int c_size, r_size; double **p;};
74
Overloading () (2 of 5)
matrix:: matrix(int c, int r):c_size(c), r_size(r){ assert (c > 0 && r > 0); p = new double*[c]; assert (p); for (int i = 0; i < c; ++i) { p[i] = new double[r]; assert (p); }}
75
Overloading () (3 of 5)
matrix:: ~matrix(){ for (int i = 0; i < c_size; ++i) delete [] p[i]; delete [] p;}
76
Overloading () (4 of 5)
matrix& matrix::operator=(const matrix& m){ assert(m.c_size == c_size && m.r_size == r_size);
int i, j;
for (i = 0; i < c_size; ++i) for (j = 0; j < r_size; ++j) p[i][j] = m.p[i][j];
return (*this);}
77
Overloading () (5 of 5)
matrix& matrix::operator+= (const matrix& m){ assert(m.c_size == c_size && m.r_size == r_size);
int i, j;
for (i = 0; i < c_size; ++i) for (j = 0; j < r_size; ++j) p[i][j] += m.p[i][j];
return (*this);}
78
Comments on the matrix Program (1 of 2)
The constructor first allocates an array of pointer-to-double off the heap, then each array of double is allocated with its base address stored in a corresponding element p[i]
This scheme is necessary to provide correct addressing to individual matrix components regardless of size
The overloaded member function () gives a convenient multiple argument notation for element access which results in client code using expressions of the form m(i,j) to access explicit matrix elements
79
Comments on the matrix Program (2 of 2)
Through an assertion or conditional statement, matrix indices are bounds-tested
Assertion macro is used with a precondition for arguments needed by the member function
The assertion code replaces an if-else statement that would perform an error exit
The matrix being assigned to must be the same size as the matrix expression being computed
Dereferencing the this pointer causes the lvalue of the matrix object to be returned
80
Overloading new and delete
Many classes involve free store memory allocation and deallocation
The user of a class wants it to be as flexible and general as possible which can require more sophisticated use of memory than is provided by simple calls to operator new and delete
Operators new and delete can be overloadedProvides a simple mechanism for user-defined
manipulation of free store
81
Memory Management Overloading
new gets memory
delete gets rid of memory
Both can be overloaded
82
Overloading new and delete
// Title: alloc//malloc() and free() defined
#include <stdlib.h>
class X { . . .public: void* operator new(size_t size) { return (malloc(size)); } void operator delete(void* ptr) { free(ptr); } X(unsigned size) { new(size); } ~X() { delete(this); } . . .};
83
Comments on the alloc Program
Overloaded forms of new() and delete() When a class overloads operator new(), the
global operator is still accessible using the scope resolution operator ::
One reason to overload these operators is to give them additional semantics
Provides diagnostic information or fault tolerance
The class can have a more efficient memory allocation scheme than provided by the system
Allocate specific memory pool
Defer deallocation and deallocate for a list of objects
84
Placement Syntax and new
Placement syntax provides a comma-separated argument list used to select an overloaded operator new() with a matching signature
Additional arguments are often used to place the constructed object at a particular address
This form of new uses the new.h header file
Placement syntax allows the user to have an arbitrary signature for overloaded new operator
This signature is distinct from initializer arguments used by calls to new that select an appropriate constructor
85
Placement Syntax and new overloaded
#include <iostream.h>#include <new.h>
char* buf1 = new char[1000]; //free storechar* buf2 = new char[1000];class object { . . .};
main(){ object *p = new(buf1) object; //allocate at buf1 object *q = new(buf2) object; //allocate at buf2 . . .}
86
The delete Operator
The delete operator comes in two flavors void operator delete(void* p); void operator delete(void* p, size_t);
The first signature makes no provision for the number of bytes to be returned by delete
Programmer provides code that supplies this value
The second signature includes a size_t argument passed to delete which is provided by the compiler as size of object pointed at by p
Only one form of delete can be provided as a static member function in each class
87
Using the new.h File
The new.h file has the function pointer _new_handler that calls the error handler for operator new
If memory is exhausted, the function pointer _new_handler is calls a default system routine
The user can specify an explicit out of free store routine, which can replace the default by using set_new_handler()
It is likely in future systems that new will throw an exception when free store is exhausted
88
Simple Fault Tolerance: _new_handler
#include <new.h>
void heap_exhausted() //user-defined error handling{ cerr << "HEAP EXHAUSTED" << endl; exit(1);}
main(){ set_new_handler(&heap_exhausted); . . .//memory exhaustion is treated//heap_exhausted()};
89
Comments on Simple Fault Tolerance
These class new() and delete() member functions are always implicitly static
new() is invoked before the object exists and therefore cannot have a this yet
delete() is called by the destructor, so the object is already destroyed
90
More Signature Matching
The function argument type list is called its signature and order of the arguments is crucial
int sqr(int i); //int illegaldouble sqr(int i); //int illegalvoid print(int i = 0); //intvoid print(int i, double x); //int, doublevoid print(double y, int i); //double, int
When the print function is invoked, the compiler matches the actual arguments to the different signatures and picks the best match
91
In general there are three possibilities:
a best match
an ambiguous match
no match
Without a best match, the compiler issues an appropriate syntax error
Match Possibilities
92
2-Step Matching Algorithm
The matching algorithm has two parts
The first part determines a best match for each argument
For a given argument a best match is always an exact match
An exact match also includes trivial conversions
The second part sees if there is one function that is a unique best match in each argument
93
Match Types
void print(int i = 0);void print(int i, double x);void print(double y, int i);
print(15); matches intprint('A'); converts and matches intprint(9.90); converts and matches intprint(str[]); no match wrong typeprint(15, 9); ambiguousprint(15.0, 9); matches double, intprint(15, 9.0); matches int, doubleprint(15.0, 9.0); ambiguousprint(i, j, k); no match too many argumentsprint(); match int by default
94
Match Examples
Equally Good Not as Good T T& T* const T* T& T T* volatile T* T const T T& const T& T volatile T T& volatile T& T[] T* T(args) (*T)(args)
The six left-hand trivial conversions cannot be used to disambiguate exact matches
95
Promotions and Matching
The matching rule distinguishes promotions from other standard conversions
Promotion goes from narrow type to wider typeGoing from char to int is a promotion
Promotions are better than other standard conversions
Among promotions, conversion from float to double and conversion from char, short, or enum to int are better than other promotions
Standard conversions include pointer conversions, explained for inheritance
96
User-Defined Conversions and Matching
User-defined conversions include constructors of a single argument
This constructor can be implicitly called to perform a conversion from the argument type to its class type
This can happen for assignment conversions, as in the argument-matching algorithm
97
Conversions & Matching clock (1 of 2)
//clock with a reset function
class clock {private: unsigned long int tot_secs, secs, mins, hours,days;public: //constructor & conversion clock(unsigned long int i); void print(); //formatted printout void tick(); //add one second clock operator++() {this —> tick(); return(*this);} void reset(const clock& c); //alternate to operator=()};
98
Conversions & Matching clock (2 of 2)
void clock::reset(const clock& c){ *this = c;}
main(){ clock c1(900), c2(400); . . . c1.reset(c2); c2.reset(100); . . .}
99
Comments on the clock Match
The call to reset(100) involves an argument match between int and clock that is a user-defined conversion invoking the constructor clock(unsigned)
Explicitly casting arguments can be both an aid to documentation and a useful way to avoid poorly understood conversion sequences
100
Design of a Polynomial Class
What is the public behavior of the ADT?What implementation(s) should be used?
Limits?Efficiency?
What should its relationship be to other types?Explicit conversions?
Implicit conversions?
What is expected use of operator overloading?
Inheritance and friendship relationshipsWhere are special privileges required?
Extensibility concerns?
101
Polynomial & Overload Operators (1 of 4)
class poly {public: poly(); poly(const poly& p); poly(int size, double coef[], int expon[]); ~poly() { release(); } void print() const;
//evaluate P(x) double operator()(double x) const;
102
Polynomial & Overload Operators (2 of 4)
//Overloaded Operators poly& operator=(const poly& a); friend poly& operator+ (const poly& a, const poly& b); friend poly& operator— (const poly& a, const poly& b); friend poly& operator* (const poly& a, const poly& b); friend poly& operator/ (const poly& a, const poly& b); friend poly& operator—(const poly& a);//unary friend poly& operator+= (const poly& a, const poly& b);
103
Polynomial & Overload Operators (3 of 4)
friend boolean operator== (const poly& a, const poly& b); friend boolean operator!= (const poly& a, const poly& b);private: term* h; int degree; void prepend(term* t); void add_term(term*& a, term*& b); void release(); void rest_of(term* rest); void reverse();
};
104
Polynomial & Overload Operators (4 of 4)
poly& poly::operator=(const poly& a){ if (h != a.h) { //avoid a = a case release(); //garbage collect old value poly* temp = new poly(a); h = temp —> h; degree = temp —> degree; } return (*this);}
105
Comments on the poly Class
We expect both the basic mathematical operations to work and the basic relationships among C++ operators to hold
It would be very undesirable to have operator=(), operator+(), and operator+=() all defined and not have a = a + b give the same result as a += b
106
Summary of Ad Hoc Polymorphism (1 of 4)
A functional notation type-name (expression) is equivalent to a cast
A constructor of one argument is de facto a type conversion from the argument's type to the constructor's class type
A conversion from a user-specified type to a built-in type can be made by defining a special conversion function
Conversions occur implicitly in assignment, arguments to functions, and values returned from functions
107
Summary of Ad Hoc Polymorphism (2 of 4)
The overloaded meaning is selected by matching the argument list of the function call to the argument list of the function declaration
The algorithm that accomplishes this depends on what type conversions are available
1. Use an exact match if found
2. Try standard type promotions
3. Try standard type conversions
4. Try user-defined conversions
5. Use a match to ellipsis if found
108
Summary of Ad Hoc Polymorphism (3 of 4)
Keyword friend is a function specifier and it allows a nonmember function access to hidden members of the class of which it is a friend
A friend function or an ordinary function has both arguments specified in the parameter list
Overloading operators gives them new meanings for ADTs: the ADT can then be used in much the same way as a built-in type
Operator overloading uses either member functions or friend functions because they have privileged access
109
Summary of Ad Hoc Polymorphism (4 of 4)
When a unary operator is overloaded using a member function, it has an empty argument list: single operator argument is implicit
When a binary operator is overloaded using a member function, its 1st argument is the implicitly passed class variable and its 2nd is the lone argument list parameter
Overloaded subscript, assignment, function call, and member access must be non-static member functions
new and delete can be overloaded for user-defined manipulation of free store
110
110
2
C++ I/O Streams
C++ introduces iostream.h, the standard C++ library containing the input/output functions
The stream I/O is described as a set of classes in iostream.h
iostream.h overloads the put-to (insert) andget-from (extract) operators << and >>
Streams can be associated with files
110