Sunday, January 8, 2012

Operator Oveloading




Basics of operator overloading
1. Overloading binary operators
2. Overloading the relational and logical operators
3. Overloading a unary operator
4. Using friend operator functions
5. A closer look at the assignment operator
6. Overloading the [ ]subscript operator


BASICS OF OPERATOR OVERLOADING
1.Allows the programmer to define the meaning of
the C++ operators relative to programmer
defined classes
2. Resembles function overloading
3. An operator is always overloaded relative to a
user-defined type, such as a class
4.When overloaded, the operator loses none of its
original meaning
5. To overload an operator, we create an operator
function
6. An operator function can be
-A member of the class for which it is defined
-A friend of the class for which it is defined

General form of a member operator function
return-type class-name::operator#(arg-list) { … }

Restrictions:
The precedence of the operator cannot be changed
The number of operands that an operator takes cannot be altered
The following operators cannot be overloaded
. :: .* ? preprocessor operators
Except for the =, operator functions are inherited by
any derived class.
Operator functions can be further overloaded in the
derived classes.
Operator functions cannot have default arguments.
Running Codes
class coord {
int x, y;
public:
coord(int a = 0, int b = 0) {
x = a; y = b;
}
void show() {
cout << x << “, ” << y <<
endl;
}
coord operator+(coord obj);
coord operator+(int i);
coord operator-(coord obj);
coord operator=(coord obj);
};

coord coord::operator+(coord
obj) {
coord temp;
temp.x = x + obj.x;
temp.y = y + obj.y;
return temp;
}
coord coord::operator+(int i) {
coord temp;
temp.x = x + i;
temp.y = y + i;
return temp;
}
coord coord::operator-(coord obj) {
coord temp;
temp.x = x - obj.x;
temp.y = y - obj.y;
return temp;
}
coord coord::operator=(coord obj) {
x = obj.x;
y = obj.y;
return *this;
}


Reference




A reference is an implicit pointer
* Acts like another name for a variable
* Can be used in three ways
* A reference can be passed to a function
* A reference can be returned by a function
* An independent reference can be created
* Reference variables are declared using the & symbol
Example: void f(int &n);

Unlike pointers, once a reference becomes
associated with a variable, it cannot refer to
other variables

Advantages
1. The address is automatically passed
2.Reduces use of ‘&’ and ‘*’
3.When objects are passed to functions using
references, no copy is made
Hence destructors are not called when the
functions ends
Eliminates the troubles associated with
multiple destructor calls for the same object
Running Codes
Using pointer:
void f(int *n) {
*n = 100;
}
void main() {
int i = 0;
f(&i);
cout << i; // 100
}

Using reference :
void f(int &n) {
n = 100;
}
void main() {
int i = 0;
f(i);
cout << i; // 100
}
http://en.wikipedia.org/wiki/Reference_%28C%2B%2B%29

Initialization, D estruction, and Copy

Now that num_sequence declares actual data members, we must provide for their initialization.
We could leave it to each derived class to initialize these data members, but that's potentiallyerror-prone. A better design is to provide a base class constructor to handle the initialization of all
base class members.
Recall that num_sequence is an abstract base class. We cannot define an independent object of its
class; rather, the num_sequence serves as a subobject of each derived cla ss object. For this reason,
we declare the base class constructor to be a protected rather than public member.
The initialization of a derived class object consists of the invocation of the base class constructor
followed by that of the derived class constructor. It helps to think of the derived class object as
consisting of multiple subobjects: a base class s ubobject initialized by the base class constructor
and a derived class subobject initialized by the derived class constructor. In a three-level class
hierarchy, such as the AudioBook class of Section 5.1, the derived class consists of three
subobjects, each one initialized by its respective constructor.
The design requirements of a derived class constructor are twofold: Not only must it initialize the
derived class data members, but it must also supply the expected values to its base class
constructor. In our example, the num_sequence base class requires three values that are passed to
it using the member initialization list. For example,


If we should overlook the invocation of the num_sequence constructor, the definition of the
Fibonacci constructor is flagged as an error. Why? The num_sequence base class requires our
explicit invocation of its three-argument constructor. In our design, this is what we want.
Alternatively, we could provide a default num_sequence constructor. We must change _relems
to a pointer, however, and add code to verify th at it is non-null before each access of the vector:


Running Codes
inline Fibonacci::
Fibonacci(int len, int beg_pos)
: num_sequence(len, beg_pos, &_elems)
{}


num_sequence::
num_sequence(int len=1, int bp=1, vector *pe=0)
: _length(len), _beg_pos(bp), _pelems(re){}

Exception Part 2(Example)





Running Codes
// exceptions_Exception_Examples.cpp
// compile with: /EHsc
#include

using namespace std;
void MyFunc( void );

class CTest
{
public:
CTest(){};
~CTest(){};
const char *ShowReason() const { return "Exception in CTest class."; }

};

class CDtorDemo
{
public:
CDtorDemo();
~CDtorDemo();
};

CDtorDemo::CDtorDemo()
{
cout << "Constructing CDtorDemo." << endl;
}

CDtorDemo::~CDtorDemo()
{
cout << "Destructing CDtorDemo." << endl;
}

void MyFunc()
{

CDtorDemo D;
cout<< "In MyFunc(). Throwing CTest exception." << endl;
throw CTest();
}

int main()
{
cout << "In main." << endl;
try
{
cout << "In try block, calling MyFunc()." << endl;
MyFunc();
}
catch( CTest E )
{
cout << "In catch handler." << endl;
cout << "Caught CTest exception type: ";
cout << E.ShowReason() << endl;
}
catch( char *str )
{
cout << "Caught some other exception: " << str << endl;
}
cout << "Back in main. Execution resumes here." << endl;
return 0;
}
http://www.cprogramming.com/tutorial/exceptions.html

Using an Inheritance Hierarchy

Let's presume we've defined the five other numeric sequence classes (Pell, Lucas, Square,
Triangular, and Pentagonal) in the same manner as the Fibonacci class. We now have a two-level
inheritance hierarchy: an abstract num_sequence base class and the six inheriting derived classes.
How might we use them?
Here is a simple display() function whose second parameter is ns, a const reference to a
num_sequence object.


Within display(), we call the two virtual functions what_am_i() and elem() . Which
instances of these functions are invoked? We cannot say for certain. We know that ns does not
refer to an actual num_sequence class object but rather to an object of a class derived from
num_sequence. The two virtual function calls are resolved at run-time based on the type of theclass object ns refers to. For example, in the following small program I define an object of each
derived class in turn and pass it to display():


Running Codes
inline void display(ostream &os,
const num_sequence &ns, int pos)
{
os << "The element at position "
<< pos << " for the "
<< ns.what_am_i() << " sequence is "
<< ns.elem(pos) << endl;
}


int main()
{
const int pos = 8;
Fibonacci fib;
display(cout, fib, pos);
Pell pell;
display(cout, pell, pos);
Lucas lucas;
display(cout, lucas, pos);
Triangular trian;
display(cout, trian, pos);
Square square;
display(cout, square, pos);
Pentagonal penta;
display(cout, penta, pos);
}

Defining an Abstract Base Class

In this section we redesign the num_sequence class of the preceding section in to an abstract base
class from which we inherit each of the numeric sequence classes. How do we go about that?
The first step in defining an abstract base class is to identify the set of operations common to its
children. For example, what are the operations common to all numeric sequence classes? These
operations represent the public interface of the num_sequence base class. Here is a first iteration:


elem() returns the element at the user-requested position. max_elems() returns the maximum
number of elements supported by our implementation. check_integrity() determines
whether pos is a valid position. print() displays the elements. gen_elems() generates the
elements for the sequence. what_am_i() returns a character string identifying the sequence. The next step in the design of an abstract base class is to identify which operations are type-dependent — that is, which operations require separate implementations based on the derived
class type. These operations become the virtual func tions of the class hierarchy. For example, each
numeric sequence class must provide a separate implementation of gen_elems() .
check_integrity(), on the other hand, is type-invariant. It must determine whether pos is a
valid element position. Its algorithm is independent of the numeric sequence. Similarly,
max_elems() is type-invariant. All the numeric sequences hold the same maximum number of
elements.
Not every function is this easy to distinguish. what_am_i() may or may not be type-dependent
depending on how we choose to implement our inheritance hierarchy. The same is true of elem()
and print(). For now, we'll presume that they are type-dependent. Later, we'll see an alternative
design that turns them into type-invariant functions. A static member function cannot be declared
as virtual.
The third step in designing an abstract base class is to identify the access level of each operation.
If the operation is to be available to the general program, we declare it as public . For example,
elem() , max_elems() , and what_am_i() are public operations.
If the operation is not meant to be invoked outside the base class, we declare it as private. A
private member of the base class cannot be accessed by the classes that inherit from the base class.
In this example, all the operations must be available to the inheriting classes, so we do not declare
any of them as private.
IA third access level, protected, identifies operations that are available to the inheriting classes
but not to the general program. check_integrity() and gen_elems() , for example, are
operations that the inheriting classes, but not the general program, must invoke. Here is our
revised num_sequence class definition:


Running Codes
class num_sequence {
public:
// elem(pos): return element at pos
// gen_elems(pos): generate the elements up to pos
// what_am_i() : identify the actual sequence
// print(os) : write the elements to os
//check_integrity(pos) : is pos a valid value?
// max_elems() : returns maximum position supported
int elem(int pos);
void gen_elems(int pos);
const char* what_am_i() const;
ostream& print(ostream &os = cout) const;
bool check_integrity(int pos);
static int max_elems();
// ...
};


class num_sequence {
public:
virtual ~num_sequence(){};
virtual int elem(int pos) const = 0;
virtual const char* what_am_i() const = 0;
static int max_elems(){ return _max_elems; }
virtual ostream& print(ostream &os = cout) const = 0;
protected:
virtual void gen_elems(int pos) const = 0;
bool check_integrity(int pos) const;
const static int _max_elems = 1024;
};

Exception Part1




Exceptions are run time anomalies or unusual
conditions that a program may encounter during
execution.
Conditions such as
*Division by zero Division by zero
*Access to an array outside of its bounds
*Running out of memory
*Running out of disk space
It was not a part of original C++.
It is a new feature added to ANSI C++.

Exceptions are of 2 kinds
1.Synchronous Exception:
Out of rage
Over flow
2.Asynchronous Exception: Error that are caused by
causes beyond the control of the program
Keyboard interrupts
In C++ only synchronous exception can be
handled.
Exception handling mechanism
Find the problem (Hit the exception)
Inform that an error has occurred (Throw the exception) Inform that an error has occurred (Throw the exception)
Receive the error information (Catch the exception)
Take corrective action (handle the exception)
EXCEPTION HANDLING MECHANISM

It is basically build
upon three keywords
Try
Throw
Catch
try block Detects and throws an exception
Catch
catch block
Catch

The keyword tryis used to preface a block of
statements which may generate exceptions.
When an exception is detected, it is thrown using a
throwstatement in the try block. throwstatement in the try block.
A catchblock defined by the keyword ‘catch’
catches the exception and handles it appropriately.
The catch block that catches an exception must
immediately follow the try block that throws the
exception.
EXCEPTIONHANDLINGMECHANISM(CONT…)
try
{

throw exception;

// Block of statements
// which detect and
// throws an exception …
}
catch(type arg)
{



}
// throws an exception
// catch exception
// Block of statement
// that handles the
// exception
Running Codes
http://www.cprogramming.com/tutorial/exceptions.html