1 / 110

# Topics on Ad Hoc Polymorphism - PowerPoint PPT Presentation

I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.

- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
• Class-Defined Conversions
• Friend Functions
• More Signature-Matching
• Polynomial:Type And Language Expectations

Go get

me a ball

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
Type Conversions
• Conversion is the explicit or implicit change of value between types
• Conversions provide a form of polymorphism

Float Int

• 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)
• 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
New Cast Notation
• A functional notation is equivalent to a cast
• type-name (expression)
• The type must be expressible as an identifier
Equivalent Expressions and Casting
• x = float(i); //C++ functional notation
• x = (float) i; //equivalent to above
• p = (int*) q; //legal cast
• p = int*(q); //illegal
• typedef int* int_ptr; //equivalent
• p = int_ptr(q);
• Functional notation is the preferred style
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
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
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 implicitly
• in assignment expressions
• in arguments to functions
• in values returned from functions
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 *
Explicit & Implicit String Conversions
• string s;
• char* logo = "Geometrics Inc";
• //perform explicit conversion then assignment
• s = string(logo);
• //implicit invocation of conversion
• s = 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
Ambiguity and Conversions
• A conversion member function of the form A::operator B() and a constructor of the formB::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

print(int)

print(int, int)

print(foo_bar)

• 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

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

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
Function Selection Algorithm In Use

Declarations

• int i;
• double w;
• complex z;
• char c, *p;
• void f(int);

f(i); exact match

f(c); standard promotion

f(w); standard conversion

f(z); user-defined promotion

f(p); illegal

• // Title: greater
• #include <iostream.h>
• #include <math.h> //for sqrt
• class 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;
• };
• 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); }
• 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 " ;
• 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;
• }
• The output from this program is
• compare 10 and 5 greater is 10
• compare 7 and 14.5 greater is 14.5
• compare 14.5 and 10 + 5i greater is 14.5
• compare 7 + 14.5i and 10 + 5i greater is 7 + 14.5i
• 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;
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

Show membership

card to get in

Letting Non-members in

I’m a friend, so let me in without a membership card, please.

OK, come in.

Reasons for Using Friend Functions
• Friend access follows certain understood situations that would othrwise be inconvenient
• 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
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 classclass-name
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
Assignment Compatible Conversion
• class complex {
• . . .
• friend complex operator+(complex, complex);
• . . .
• };
• 1 + w; //with member function this fails
• w + 1; //this is OK either way
• Symmetry of application of assignment compatible conversion rules for arguments is desirable
Separating Components into Classes
• class tree;
• class node {
• . . .
• friend class tree;
• . . .
• };
• A special relationship holds between whole and part thatfavors separating thesecomponents
Sub-component Access
• friend vect mpy(const vect&, const matrix&);
• More generally, classes must interact intimately
Using Friends
• 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
Using Friends (1 of 3)
• // Title: matrix_v
• class matrix; //forward reference
• class vect {
• public:
• friend vect mpy(const vect& v, const matrix& m);
• . . .
• private:
• int* p;
• int size;
• };
Using Friends (2 of 3)
• class matrix { //stores integer elements
• public:
• friend vect mpy(const vect& v, const matrix& m);
• . . .
• private:
• int** base;
• int row_size, column_size;
• };
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
• 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);
• }
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

40

40

41

41

• 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

43

43

45

45

46

46

47

47

48

48

• 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
• 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
• 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
• 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
• 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
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);
• }
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
Using the Binary clock Operations
• int i = 5;
• clock c(900), d(800);
• c + i //legal: i converted to a clock
• i + c //legal: i converted to a clock
• c — i //legal: i converted to a clock
• c.operator—(i) //function call notation
• i — c //illegal: i is not a clock
• i.operator—(c) //illegal: function call notation
• i * 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
Lvalues and Rvalues
• A =B
• c[i]=5 + D[j]
• 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
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
Making a Safe Array

There isn’t a

6th one to change!

Let’s

change the

6th vector

element.

5

7

15

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
• private:
• int* p; //base pointer
• int size; //number of elements
• };
Safe Array with Subscripting (2 of 6)
• //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];
• }
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];
• }
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]);
• }
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);
• }
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);
• }
• 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
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
• 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
• Meaningful with the extended class vect:
• a = b; //a, b are type vect
• a = b = c; //a, b, c are type vect
• a = vect(data, DSIZE); //convert array data[DSIZE]
• a = b + a; //assignment and addition
• a = 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
• 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
• // 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;
• };
• 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);
• }
• }
• matrix:: ~matrix()
• {
• for (int i = 0; i < c_size; ++i)
• delete [] p[i];
• delete [] p;
• }
• 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);
• }
• 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);
• }
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
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
• 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 overloaded
• Provides a simple mechanism for user-defined manipulation of free store
• new gets memory
• delete gets rid of memory
• // 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); }
• . . .
• };
• 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
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
• #include <iostream.h>
• #include <new.h>
• char* buf1 = new char[1000]; //free store
• char* buf2 = new char[1000];
• class object {
• . . .
• };
• main()
• {
• object *p = new(buf1) object; //allocate at buf1
• object *q = new(buf2) object; //allocate at buf2
• . . .
• }
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
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
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()
• };
• 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
More Signature Matching
• The function argument type list is called its signature and order of the arguments is crucial
• int sqr(int i); //int illegal
• double sqr(int i); //int illegal
• void print(int i = 0); //int
• void print(int i, double x); //int, double
• void 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
Match Possibilities
• 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
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
Match Types
• void print(int i = 0);
• void print(int i, double x);
• void print(double y, int i);
• print(15); matches int
• print(\'A\'); converts and matches int
• print(9.90); converts and matches int
• print(str[]); no match wrong type
• print(15, 9); ambiguous
• print(15.0, 9); matches double, int
• print(15, 9.0); matches int, double
• print(15.0, 9.0); ambiguous
• print(i, j, k); no match too many arguments
• print(); match int by default
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
Promotions and Matching
• The matching rule distinguishes promotions from other standard conversions
• Promotion goes from narrow type to wider type
• Going 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
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
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=()
• };
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);
• . . .
• }
• 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
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?
• Inheritance and friendship relationships
• Where are special privileges required?Extensibility concerns?
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;
Polynomial & Overload Operators (2 of 4)
• 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);
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();
• };
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);
• }
• 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
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
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
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
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