function overloading c++ supports writing more than one ... · overloading operators in c++ • c++...
TRANSCRIPT
1
Function Overloading
• C++ supports writing more than one function with the same name but different argument lists
• How does the compiler know which one the programmer is calling? – They have different signatures
– A function’s signature is a combination of its name and its argument list
– the return type is not part of the signature – so you can’t overload just by changing return type
2
When you use function overloading, ambiguity can
happen in 3 cases:
1. if you have call by reference and call by value with the same parameters’ types:
void square (double& x)
{ x = x * x; }
void square (double x)
{ cout << x * x; }
2. if there is no matching parameter type for a called function but variables can be automatically converted to appropriate types:
int area (int height, int width)
{ return height * width; }
double area (double height, double width)
{ return height * width; }
3. if you use default arguments: prototypes:
void fcn(int a, int b = 9, double c = 1.33);
void fcn(int a, int b);
3
Function Overloading (cont.)
Suppose that we have a scenario such as: void swap (int *a, int *b);
void swap (double *x, double *y);
void swap (bool *p, bool *q);
int main()
{
int i = 4, j = 6;
double c = 13.2, d = 43.1;
bool flag1 = TRUE, flag2 = FALSE;
swap (&i, &j);
swap (&c, &d);
swap (&flag1, &flag2);
}
with function definitions as shown on the next slide:
4
void swap (int *a, int *b)
{
int temp;
temp = *a;
*a = *b;
*b = temp;
}
void swap (double *x, double *y)
{
double temp;
temp = *x;
*x = *y;
*y = temp;
}
void swap (bool *p, bool *q)
{
bool temp;
temp = *p;
*p = *q;
*q = temp;
}
Each of these functions
expects to see pointers as
arguments –
the only difference is what
kind of thing the pointer
argument is pointing to
• The compiler matches up the
signature in the function
definition with the call so that the
correct version gets invoked
5
Remember Function Templates
• No need for 3 different functions
• We could create a function template definition for swap
– the function template sets up the framework for the functions we
need, then based on how the template is called, the compiler
generates the necessary code
Example:
template <typename T> void swap(T *a, T *b)
{
T temp;
temp = *a;
*a = *b;
*b = temp;
}
the keyword template tells the compiler it’s a function template
everywhere a T occurs in the template,
the compiler will substitute the correct
type depending on the call to the function
this is the function’s return type – here
it is void because the original swap
functions had void return type
6
One More (trivial) Template Example -
template <class T> T square (T x)
{
return x * x;
}
if called with an int argument, this will return an int
result; if called with a double argument, it will return a
double.
7
Object Oriented Design
Where do we stand now?
• We’ve talked about classes and their definition – class definition in <classname>.h file
• this defines the interfaces for the class
– implementation of the methods in <classname>.cpp file
• We’ve talked about constructors and destructors – default constructor and overloaded constructors
– issues to consider when a class has a member that is dynamic
– order of method invocation as a program runs (explored through a trace of what happens in an example using the point class)
• We discussed copy constructors and saw an example
8
Just to remind you:
Constructors: What Happens by Default
• If you do not define a constructor, the system will define a default constructor
• the default constructor is supposed to initialize all numeric types to 0, all pointers to null, and all Boolean types to false (but be careful – check that it really does this!)
• If you do define a constructor, yours will be used whenever an object of this type is instantiated – that is, whenever an object is declared or new or gcnew is called
to create an object of this type
• If you provide a constructor that takes a nonvoid parameter, you must also provide a default constructor.
• objects of managed classes must always be declared as tracking handles (otherwise you’ll get a compile error)
9
Destructors: What Happens by Default
• A destructor is a function that: – Is automatically invoked when an object is destroyed
• i.e. at the end of the block in which an object was declared
– Performs termination housekeeping • In particular, it recovers memory to be reused
• On block exit, destructors for objects declared in the block are called in reverse order of object instantiation (see class example)
– Takes no arguments and returns no value
– Has the same name as the class preceded by a ~
• There is only one destructor for a class – no overloading is possible
• The system automatically generates a destructor for a class, but … – Programmers do have to write a destructor when their class
dynamically allocates memory (which can happen when data members are pointers or array names or CLI objects)
10
= Operators: What Happens by Default
• Ordinarily, the compiler will generate a default
assignment operator
– a point class (that has two double data members) is
an example of a case in which the = operator is
automatically overloaded by the compiler
• If you define any assignment operator that takes
the class as a parameter, the compiler cannot
generate a default assignment operator for that
class.
• In these cases, you have to provide an
overloaded assignment operator
11
Why do we need a copy constructor?
• If a copy constructor is missing (for any class),
the compiler creates a default one that performs
member-by-member copying.
• For classes that have dynamic members, we
need to have appropriate memberwise copy
functions (we’ve seen an example of why)
• Let’s revisit the class MemberInfo that we looked
at before
– We’re going to look at 4 different ways to represent
the same information – then consider how statements
execute differently (and why)
12
Class MemberInfo header: #pragma once
#ifndef MEMB1
#define MEMB1
using namespace System;
ref class MemberInfo{
public:
MemberInfo(); // default constructor
MemberInfo(MemberInfo %); // copy constructor
void enterName(); // method for entering name
void enterAge(); // method for entering age
String^ getName(); // method for extracting name
int getAge(); // method for extracting age
void setName(String^); // method for setting name to a value
void setAge(int); // method for setting age to a value
void display(); // method for display of the object’s data
~MemberInfo(); // destructor
private:
String ^name;
int age;
static int count;
};
#endif
// MANAGED CLASS
ref class MemberInfo{
public:
static int count;
MemberInfo();
MemberInfo(MemberInfo %);
void enterName();
void enterAge();
String^ getName();
int getAge();
void setName(String^);
void setAge(int);
void display();
~MemberInfo();
private:
String ^name;
int age;
};
// STANDARD C++ CLASS
// copy constructor w/ ref parameter
class MemberInfo{
public:
static int count;
MemberInfo();
MemberInfo(MemberInfo &);
void enterName();
void enterAge();
string* getName();
int getAge();
void setName(string*);
void setAge(int);
void display();
~MemberInfo();
private:
string * name;
int age;
};
M_Info Native M_Info2
// STANDARD C++ CLASS
// pointer to string type data member
class MemberInfo{
public:
static int count;
MemberInfo();
MemberInfo(MemberInfo *);
void enterName();
void enterAge();
string* getName();
int getAge();
void setName(string*);
void setAge(int);
void display();
~MemberInfo();
private:
string * name;
int age;
};
// STANDARD C++ CLASS
// string type data member
class MemberInfo{
public:
static int count;
MemberInfo();
MemberInfo(MemberInfo &);
void enterName();
void enterAge();
string getName();
int getAge();
void setName(string);
void setAge(int);
void display();
~MemberInfo();
private:
string name;
int age;
};
Native M_Info1 Native M_Info
What happens with = and ==? • M_Info
– Has members • MemberInfo(MemberInfo %);
• String ^name;
– Does not allow this sequence of statements • MemberInfo ^n1, ^n2;
• *n1 = *n2; // error C2582: 'operator =' function is unavailable
– Allows this sequence of statements: • if (n1 == n2) cout << "equal \n";
• Native M_Info
– Has members • MemberInfo(MemberInfo &);
• string name;
– Allows this sequence of statements: • MemberInfo *n1, *n2;
• *n1 = *n2;
– Does not allow • if (n1 == n2) cout << "equal \n";
– // error C2678: binary '==' : no operator found which takes a left-hand operand of type 'MemberInfo' (or there is no acceptable conversion)
Executes the default assignment operator
Why should this allow ==?
What happens with = and ==?
• Native M_Info 1 – Has members
• MemberInfo(MemberInfo *);
• string * name;
• int age;
– Allows • MemberInfo *n1, *n2;
• *n1 = *n2;
• if (n1 == n2) cout << "equal \n";
• Native M_Info 2 – Has members
• MemberInfo(MemberInfo &);
• string* name;
• int age;
– Allows • MemberInfo *n1, *n3;
• *n1 = *n3;
• if (n1 == n3) cout << "equal \n";
Why should this allow assignment and ==?
Why should this allow assignment and ==?
Moral of the story: Be sure you know what = (assignment) and == (equality test)
really mean for your class!
Overloading Operators in C++
• C++ allows the programmer to redefine the function of
most built-in operators on a class-by-class basis
• the operator keyword is used to declare a function that
specifies what an operator symbol (such as = or +)
means when it is applied to instances of a class
– this gives the operator more than one meaning, and the compiler
determines what meaning is intended by looking at the types of
its operands
• syntax:
type operator <operator-symbol> (parameter-list)
• Example: overloading the addition operator and the
assignment operator for a class Complex that is intended
to represent complex numbers
Example
Class to represent complex numbers: header (class definition file)
#pragma once
#include <iostream>
using namespace System;
using namespace std;
class Complex
{
public:
Complex ();
Complex (double r, double i);
Complex operator+ (Complex &other);
Complex & Complex::operator = (Complex &n);
void display();
private:
double re,im;
};
.cpp source (implementation) file for for class Complex
#include "stdafx.h"
#include " Complex.h"
Complex::Complex (){
re = 0.0;
im = 0.0;
}
Complex::Complex(double r, double i){
re = r;
im = i;
}
Complex Complex::operator + (Complex &other){
return Complex (re + other.re, im + other.im);
}
Complex & Complex::operator = (Complex &n){
re = n.re;
im = n.im;
return *this;
}
void Complex::display(){
cout << re << "," << im << endl;
}
Overloaded addition
operator
Overloaded assignment
operator
Example of a small program using class Complex:
#include "stdafx.h"
#include <iostream>
#include "Complex.h"
using namespace System;
using namespace std;
int main(array<System::String ^> ^args)
{
Complex a = Complex(1.2, 3.4);
Complex b(3.4, 5.6);
Complex c;
c = a + b;
c.display();
return 0;
}
Not every operator can be overloaded!
Operators that can be overloaded
+ - * / % ^ & |
~ ! = < > += -= *=
/= %= ^= &= |= << >> >>=
<<= == != <= >= && || ++
-- ->* , -> [] () new delete
new[] delete[]
Operators that cannot be overloaded
. .* :: ?: sizeof