7.18 C/C++ (Part 2)

Copyright©  Department of Computer Science, University of Regina
Originally written in 1998 by Zhiwei Wang
Last updated in July 2001 by Zhiwei Wang

7.18 C/C++ (Part 2)

C++ was first invented by Bjarne Stroustrup in 1980 at Bell Laboratories in Murray Hill, New Jersey. He initially called it "C with Classes." However, in 1983 the name was changed to C++. C++ is a superset of C. As a popular programming language, C has many excellent features. However, once a program exceeds 25,000 to 100,000 lines of code, it becomes too difficult to be comprehended as a whole. The purpose of C++ is to overcome this limit and provide a better way to manage larger, more complex programs, by using object oriented programming (OOP). 

7.18.9. THE C++ I/O SYSTEM

7.18.9.1. C++ I/O Basics

When a C++ program is running, the following four built-in streams are automatically opened:
 
Stream Meaning Default Device Corresponding C stream
cin standard input keyboard stdin
cout standard output screen stdout
cerr standard error output screen stderr
clog buffered version of cerr screen

The following is an example of using simple I/O streams:

Example

/*********************************************************
* 
* FILE NAME: basics.cpp *
* AUTHOR: Zhiwei Wang *
* DATE: May. 1997 *
* DESCRIPTION: An example for simple I/O stream *
***********************************************************/
#include <iostream>

using namespace std;

main(){
  char name[50];
  cout << "Please enter your name: ";
  cin >> name;
  cout << "Hello, " << name << ".\n";
  cout << "My name is Zhiwei." << endl;
  // 'endl' can be used in place of "\n"
  return 0;
}

// End of program
//***********************************************************

7.18.9.2. Formatted I/O

There are three member functions defined by ios that set the format parameters, namely:
int width(int w);
This function sets the field width. Here w is the field width. The returned value is the previous width. If a value uses less than the specified width, the field will be padded with fill character. The default fill character is space.
int precision(int p);
This function sets precision to p. The returned value is the previous precision. Precision determines the number of digits to be displayed after the decimal point. The default precision is 6.
char fill(char ch);
This function sets the fill character to ch. The previous fill character is returned.
Here is an example that illustrates these functions
/***************************************************************
FILE NAME:  example1.cpp
AUTHOR:     Zhiwei Wang
DATE:       May 1998
DESCRIPTION:  
        An example demonstrating how to use the width(), 
        precision(), and fill() functions. 
*****************************************************************/
#include <iostream>

using namespace std;

main()
{
  double pi = 3.1415926535;
  cout << pi << endl;

  cout.precision(3);
  cout << pi << endl;

  cout.precision(3);
  cout.width(10);
  cout << pi << endl;

  cout.precision(3);
  cout.width(10);
  cout.fill('0');
  cout << pi << endl;

  return 0;
}

/* Output:
mercury[105]% a.out
3.14159
3.14
      3.14
0000003.14
*/
Another way to perform formatted I/O is using manipulators. Manipulators are special functions that can be used to format i/o. In the previous example, 'endl' is a manipulator to output a newline character and flush the stream.

Some manipulators require parameters. To access manipulators that take parameters, you have to include <iomanip.h> in your program. The following is an example of using manipulators to format the output.

/*****************************************************************
FILE NAME:  example2.cpp
AUTHOR:     Zhiwei Wang
DATE:       May. 1998
DESCRIPTION:  
        An example demonstrating how to use the manipulators.
*****************************************************************/
#include <iostream>
#include <iomanip>

using namespace std;
main()
{
  int i=100;
  double pi = 3.1415926535;

  cout << i << endl;
  cout << hex << i << endl;

  cout << pi << endl;
  cout << setfill('0') << setw(10) << setprecision(3) << pi << endl;

  return 0;
}

/* Output:
100
64
3.14159
0000003.14
*/

7.18.9.3. File I/O

To perform file I/O, you must include the header file <fstream>. It defines the classes ifstream, ofstream, and fstream, which are needed to open files.
 
stream name stream type
ifstream input
ofstream output
fstream input and output

To open a file, you create a stream and then call the member function open().

Example:

/***********************************************************
* FILE NAME: readfile.cpp *
* AUTHOR: Zhiwei Wang *
* DATE: May. 1997 *
* DESCRIPTION: An example for simple disk file I/O *
************************************************************/
#include <iostream>
#include <fstream>
#include <string.h>

using namespace std;
main(){
  ifstream in;
  char filename[20];
  cout << "Enter the file name to read from: ";
  cin >> filename;
  in.open(filename);
  if (!in) {
    cout << "Input file cannot be opened.\n";
    return(1);
  }
  char str[80];
  while (!in.eof()) {
    in >> str;
    cout << str << " ";
    strcpy(str, "");
  }
  cout << "\n";
  in.close();
  return (0);
}

//END OF PROGRAM
//************************************************************/

7.18.10. CLASSES AND OBJECTS

7.18.10.1. Classes and Objects

In C++, the class forms the basis of object-oriented programming. It is the basic unit of encapsulation. Classes define the nature of objects. To create a class, use the key word class. Here is the syntax:
class class_name {
    private data and functions
access_specifier:
    data and functions
access_specifier:
    data and functions
access_specifier:
    data and functions
.
.
.
access_specifier:
  data and functions
} object_list;


Example 1

class vector {
  public:
    float x, y;
};
In this example, we define a class named vector The vector class has two data members: x and y that are float numbers. Both x and y are public, which means they are accessible to other parts of the program.

A class name can be used as a type specifier to declare objects. An object is a specific instance of a class. The following code declares three objects a, b, and c as instances of the vector class:

vector a, b, c;

Let us examine a complete program to make the preceding explanation clearer.

Example

/*********************************************************
Filename: sum.cpp Date: April 4, 1997
Author: Zhiwei Wang
Student Number: 
Description: This program calculates the sum of two vectors. 
**********************************************************/
#include <iostream>

using namespace std;

class vector {
  public:
  int x, y;
};

main(){
  vector a, b, c;
  
  a.x = 15;
  a.y = 20;
  b.x = 100;
  b.y = 40;
  c.x = a.x + b.x;
  c.y = a.y + b.y;
  cout<< "The x-coordinate of vector c is: "<< c.x<<"\n";
  cout<< "The y-coordinate of vector c is: "<< c.y<<"\n";
  return 0;
}

//Output:
//The x-coordinate of vector c is: 115
//The y-coordinate of vector c is: 420
// End of Program
//********************************************************************

7.18.10.2. Encapsulation and Information Hiding

Information hiding refers to the control of access levels of members of a class. This is done by using the three access_specifiers: public, private, and protected. A class member specified as 'public' is accessible from outside the class, while a 'private' member can be accessed only by other members of the same class, or a friend function. 'Protected' specifier is similar to 'private' as long as derived classes are not involved. We will discuss 'friend functions' and 'Derived classes' later.

Example:

/************************************************************
Filename: classes.cpp
Author: Zhiwei Wang
Date: May, 1997
Description: A simple class example
************************************************************/
#include <iostream>

using namespace std;

class person { // A simple class
  int hour;
  int rate;

public:
  int income(void); // with two methods
  void initialize(int, int);
};

int person::income(void) //Income of a person
{
  return hour * rate;
}

void person::initialize(int init_hour, int init_rate)
{
  hour = init_hour;
  rate = init_rate;
}

main(){
  person a, b;

  a.initialize(12, 10);
  b.initialize(8, 8);
  cout << "The income of person a is " << a.income() << "\n";
  cout << "The income of person b is " << b.income() << "\n";
  return 0;
}
// End of Program
//**********************************************


7.18.10.3. Constructors and Destructors

A function declared inside a class is a member function. Member functions have full access to all members of the class. A constructor is a special member function which has the same name as the class. Constructors are used to initialize the class and are automatically called when the objects are created. The complement of the constructor is the destructor. A destructor name begins with a tilde (~) character followed by the name of the class. When an object is destroyed, its destructor is automatically called. Destructors are needed because in many cases, some actions should be taken when an abject is destroyed. For example, memory spaces need to be freed, or an opened file need to be closed. Neither constructor nor destructor can return any value, so there is no return type specified for them.

Example:

/************************************************************
Filename: structor.cpp
Author: Zhiwei Wang
Date: May, 1997
Description: A simple class example with constructor and
        destructor.
************************************************************/
#include <iostream>
using namespace std;

class person { // A simple class
  int hour;
  int rate;
  
public:
  person(void);
  ~person(void);
  int income(void); // with two methods
  void initialize(int, int);
};

person:: person(void) {
  hour = 40;
  rate = 15;
}

person::~person(void) {
  hour = 0;
  rate = 0;
  cout << "Personal information deleted.\n";
}

int person::income(void) //Income of a person
{
  return hour * rate;
}

void person::initialize(int init_hour, int init_rate)
{
  hour = init_hour;
  rate = init_rate;
}

main(){
  person a, b;
  cout << "The income of person a is " << a.income() << "\n";
  cout << "The income of person b is " << b.income() << "\n";
  a.initialize(12, 10);
  b.initialize(8, 8);
  cout << "The income of person a is " << a.income() << "\n";
  cout << "The income of person b is " << b.income() << "\n";
  return 0;
}

/* output:
The income of person a is 600
The income of person b is 600
The income of person a is 120
The income of person b is 64
Personal information deleted.
Personal information deleted.
*/
// End of Program
//************************************************************

7.18.11. Variable Scope and Lifetime

7.18.11.1. Namespaces

A namespace is a mechanism by which the program can create a named scope.
A namespace definition consists of the word namespace, then an identifier, and the the body of the namespace.

Example:
// in header file cstdlib:

namespace std
{
  ...
  int abs(int);
  ...
}

Identifiers declared within the namespace body are said to
have namespce scope.  They cannot be accessed outside the
body except by using one of the following methods:

1.  using the name of the namespace, the scope resolution operator,
and the indentifier.
int a, b;
a = std::abs(b);

2. using the 'using declaration'
using std::abs;

3. using a 'using directive'
using namespace std;

Example:

// Filename: hiusingname.cpp
#include <iostream>

using namespace std;

main() {
  cout << "Hi" << endl;
}

Notice that in the preprocessor directive "#include <iostream>", the header file doesn't have the suffix .h. After the "#include", a using directive follows. When you compile this program (hiusingname.cpp) in hercules, you need to use the "LANG' option:

hercules[1]% CC hiusingname.cpp -LANG:std

If you use the older header file <iostream.h>, you don't need the using directive:

Example:

// filename: hi.cpp
#include <iostream.h>

main() {
  cout << "Hi" << endl;
}

When you compile this program (hi.cpp), you don't need to use the "LANG" option:

hercules[1]% CC hi.cpp

7.18.11.2. Variable Scope

Scope refers to the region of program code where it is legal to reference an identifier.

Class scope: The scope of class members is within the classe (struct, union)

local: The scope of an dentifiers declared inside a block starts from the point of the declaration and ends at the end of the block.

namespace scope:  The scope of identifiers declared inside a namespace is within the namespace.

global scope: The scope of an identifier declared outside all functions and classes have the global scope.

Example:

#include <iostream>

using namespace std;
 

int num = 0;  // global num

namespace myNamespace {
  int num = 1; // num in a namespace
}

void myFunction(int num);
void myFunction2();
void myFunction3();

void main() {
  cout << num << endl; // global num 0
  int num = 3;
  myFunction3(); // 0
  cout << num << endl; // 3
  cout << myNamespace::num << endl; // 1
  myFunction2(); // 4
  myFunction(num); // 9
}
// out put: 0 0 3 1 4 9 in each line

void myFunction(int num) {
  num = 9;
  cout << num << endl;
}

void myFunction3() {
  cout << num << endl;
}

void myFunction2() {
  int num = 4;
  cout << num << endl;
}

7.18.11.3. Lifetime of a Variable

The lifetime of a variable refers to the period of time during program execution when an identifier actually has memory allocated to it.
An automatic variable is one whose storage is allocated at the block entry and deallocated at the block exit.  A static variable is one whose storage remains allocated for the duration of the entire program.  All global variables are static variables.  By default, variables declared within a block are automatic variables.  However, you can use 'static' to declare a static local variable.

Static class members
If you declare a static variable in a class, all objects will share the same variable.
Example:
#include <iostream>

using namespace std;

class myClass {
  public:
    static int share;
    int num; // class
};

int myClass::share;

void main() {
  myClass o1, o2;

  o1.share = 1;
  o1.num = 1;
  cout << o1.share << endl;
  cout << o1.num << endl;
  o2.share = 2;
  o2.num = 2;
  cout << o2.share << endl;
  cout << o2.num << endl;
  cout << o1.share << endl;
  cout << o1.num << endl;
}

Member functions may also be declared as static.  There are several restrictions placed onstatic member functions:

They may only access other static members of the class.
static member functions do not have a 'this' pointer.
There cannot be a static and a non-static version of the same function.
Example
#include <iostream>

using namespace std;

class myClass {
    static int share;
    int num;
  public:
    static int getShare() {return share;}
    int getNum() {return num;}
    static void setShare(int a) { share = a; }
    void setNum(int a) { num = a;}
};

int myClass::share;

void main() {
  myClass o1;

  o1.setShare(1);
  o1.setNum(1);
  cout << o1.getShare() << endl;
  cout << o1.getNum() << endl;
  myClass::setShare(2);
  cout << o1.getShare() << endl;
  cout << o1.getNum() << endl;
}
 
 

7.18.12. FUNCTION OVERLOADING

7.18.12.1. Polymorphism

In C++, you can use the same name for two or more functions, providing that the number of parameters or the data type of parameters are different. This is called function overloading. Function overloading is one way to realize polymorphism, which means to allow one interface to be used with multiple methods.

Example

/***********************************************************
Filename: overload.cpp
Author: Zhiwei Wang
Date: July, 1997
Description: A simple example of function overloading
************************************************************/
#include <iostream>

using namespace std;

int f(float); // This function triples a float and returns int
int f(int); // This function returns the same integer
float f(float, float); // This function averages two floats

main()
{
  int num = 12;
  float num2 =12.0;
  cout << "The number is " << f(num) << "\n";
  cout << "3 times 12 is " << f(num2) << "\n";
  cout << "The average of 12 and 36 is " << f(12.0, 36.0) << "\n";
  return 0;
}

int f(int in_value) // This returns the same value
{
  return in_value;
}

int f(float in_value) // Triples a float & return int
{
  return (int)(3.0 * in_value);
}

float f(float in1, float in2) // This averages two floats
{
  return (in1 + in2)/2.0;
}

/* Output:
The number is 12
3 times 12 is 36
The average of 12 and 36 is 24
*/
// End of Program
//*************************************************
The output of the previous program shows that the C++ compiler knows which function should be invoked by analyzing the parameter lists.

7.18.12.2. Ambiguity

C++ has a feature called 'automatic type conversion', which means C++ will automatically attempt to convert the arguments used to call a function into the data type expected by the function. In some situation, this makes the compiler unable to choose between different overloaded functions. This situation is said to be ambiguous.

Let us make a small change in the program overload.cpp'. Instead of calling function 'f()' using the variables 'num' and 'num2', we use their values directly. i.e. we replace the lines

int num = 12;
float num2 =12.0;
cout << "The number is " << f(num) << "\n";
cout << "3 times 12 is " << f(num2) << "\n";
with
cout << "The number is " << f(12) << "\n";
cout << "3 times 12 is " << f(12.0) << "\n";
Compile the altered program and you will get the following:
mercury[208]% CC example2.cpp
"example2.cpp", line 16: error(3389): more than one instance of overloaded
function "f" matches the argument list:
function "f(float)"
function "f(int)"
cout << "3 times 12 is " << f(12.0) << "\n";

error detected in the compilation of "example2.cpp".
mercury[209]%

7.18.13. OPERATOR OVERLOADING

7.18.13.1. Operator Overloading Using a Member function

Many operators can be overloaded by creating operator functions. Operator functions can be either members or non members of the class that the operator will operate on. The format to create a member operator function is:
ret-type class-name::operator#(arg-list)
{
  // operation definition
}
where ret-type is the data type of the object returned by the operator. The # is a placeholder which will be replaced by the symbol of the operator. If you are overloading a unary operator, arg-list will be empty, if you are overloading a binary operator, arg-list will have one parameter, etc. Let us define a vector class and overload the + operator:

Example

/************************************************************
Filename: opover.cpp
Author: Zhiwei Wang
Date: July, 1997
Description:
overload operators using member functions.
************************************************************/
#include <iostream>

using namespace std;

class vector {
  float x, y;
public:
  vector(){};
  vector(float a, float b) {x=a; y=b;}
  void display() {cout << "x=" << x << ", y=" << y << endl;}
  vector operator+(vector v);
  float operator*(vector v);
};

vector vector::operator+(vector v) {
  vector temp;
  temp.x = v.x + x;
  temp.y = v.y + y;
  return temp;
}

float vector::operator*(vector v) {
  return (v.x * x + v.y * y);
}

main()
{
  vector f1(19, 97), f2(7, 11);
  cout << "vector 1 is: ";
  f1.display();
  cout << "vector 2 is: ";
  f2.display();
  cout << "The sum of the two vectors is: ";
  (f1+f2).display();
  cout << "The inner product of the two vectors is: " << f1 * f2 << endl;
  return 0;
}

/* The output of the program is:

vector 1 is: x=19, y=97
vector 2 is: x=7, y=11
The sum of the two vector is: x=26, y=108
The inner product of the two vector is: 1200
*/
// End of Program
//************************************************
Note that the overloaded '+' operator returns a vector, this makes the statement

(f1+f2).display();

possible.

7.18.13.2. Operator Overloading using a Friend Function

Operators can be overloaded using friend functions. When use a friend function to overload a unary operator, the function has one parameter; to overload a binary operator, two parameters; etc.

The following program rewrites the preceding example using friends to overload operators. Note that the main() function does not change.

/************************************************************
Filename: opover2.cpp
Author: Zhiwei Wang
Date: July, 1997
Description:
overload operators using friend functions
************************************************************/
#include <iostream>

using namespace std;

class vector {
  float x, y;
public:
  vector(){};
  vector(float a, float b) {x=a; y=b;}
  void display() {cout << "x=" << x << ", y=" << y << endl;}
  friend vector operator+(vector v1, vector v2);
  friend float operator*(vector v1, vector v2);
};

vector operator+(vector v1, vector v2) {
  vector temp;
  temp.x = v1.x + v2.x;
  temp.y = v1.y + v2.y;
  return temp;
}

float operator*(vector v1, vector v2) {
  return (v1.x * v2.x + v1.y * v2.y);
}

main()
{
  vector f1(19, 97), f2(7, 11);
  cout << "vector 1 is: ";
  f1.display();
  cout << "vector 2 is: ";
  f2.display();
  cout << "The sum of the 2 vectors is: ";
  (f1+f2).display();
  cout << "The inner product of the two vector is: "
       << f1 * f2 << endl;
  return 0;
}
// End of Program
//***********************************************

7.18.13.3. Restrictions in Operator Overloading

Technically one can define any activities inside an operator function. The short program gives an example.
/************************************************************
Filename: unexpected.cpp
Author: Zhiwei Wang
Date: July, 1997
Description:
************************************************************/
#include <iostream>

using namespace std;

class vector {
  float x, y;
public:
  vector(){};
  vector(float a, float b) {x=a; y=b;}
  void display() {cout << "x=" << x << ", y=" << y << endl;}
  vector operator+(vector v);
};

vector vector::operator+(vector v) {
  cout << "This is a weird addition, isn't it?\n";
  return v;
}

main()
{
  vector f1(19, 97), f2(7, 11);
  f1 + f2;
  return 0;
}

/* Output:
This is a weird addition, isn't it?
*/
// End of Program
//*************************************************
It is suggested that when you overload an operator, you have sufficient reason to do so.

There are some restrictions that apply to operation overloading.

7.18.14. INHERITANCE

7.18.14.1. Base Classes and Derived Classes

Inheritance is one of the cornerstones of object-oriented programming. It allows the members of one class to be used as if they were members of a second class. Using inheritance, you create a base class that defines members common to a set of classes, and then create derived classes to inherit the base class. The members of the base class will become the members of the derived classes. Let us examine an example:

Example

/************************************************************
Filename: 2d3d.cpp
Author: Zhiwei Wang
Date: July, 1997
Description: This program demonstrates how a derived class
(measure_3d) inherits the base class (measure_2d).
************************************************************/
#include <iostream>

using namespace std;

// base class definition
class measure_2d {
protected:
  int length, width;
public:
  void set (int a, int b) {length=a; width=b;}
  void display() { cout << "length=" << length << "\nwidth=" << width << "\n";}
  int area() { return length*width;}
};

// derived class definition
class measure_3d : public measure_2d {
  int height;
public:
  measure_3d (int a) {height=a;}
  void display_height() { cout << "height=" << height << "\n";}
  int volume() { return length * width * height;}
};

main(){
  measure_3d box(6);
  
  // access members of base class 
  box.set(2, 4); 
  box.display();
  cout << "The area occupied by the box is " << box.area() << "\n";
  
  // access members of derived class
  box.display_height();
  cout << "The volume of the box is " << box.volume() << "\n";
  return 0;
}

/* Output:
length=2
width=4
The area occupied by the box is 8
height=6
The volume of the box is 48
*/ // End of Program //*************************************************

7.18.14.2. member Access

As we have seen in the example, the general form to define a class to inherit base class is:
class derived-class-name:access base-class-name {
  // body of class
};
where 'access' is the base class access specifier, which should be either public, private, or protected. If no access specifier is explicitly presented, the default one will be assumed. If the derived class is a class, the default access specifier is private, If the derived class is struct, it is public.

Let us examine what happens when the access specifier is public, private, or protected respectively. When the specifier is public, all public members of the base class become public members of the derived class, all protected members of the base class become protected members of the derived class, but all private members remain private to the base class and are not accessible by the members of the derived class.

Let us comment out the keyword 'protected' in the definition of class measure_2d in the previous program '2d3d.cpp'. By default, 'length' and 'width' will be private to the base class measure_2d. The new program is as below:

Example

/************************************************************
Filename: 2d3d_a.cpp
Author: Zhiwei Wang
Date: July, 1997
Description: This program demonstrates how a derived class
(measure_3d) inherits the base class (measure_2d).
************************************************************/
#include <iostream>

using namespace std;

// base class definition
class measure_2d {
  // protected:
  int length, width; // length and width are private now by default
public:
  void set (int a, int b) {length=a; width=b;}
  void display() { cout << "length=" << length << "\nwidth=" << width << "\n";}
  int area() { return length*width;}
};

// derived class definition
class measure_3d : public measure_2d {
  int height;
public:
  measure_3d (int a) {height=a;}
  void display_height() { cout << "height=" << height << "\n";}
  int volume() { return length * width * height;}
  // error here: length and width is private to the base 
  // class and cannot be access here by a member of the 
  // derived class.
};

main(){
  measure_3d box(6);
  // access members of base class
  box.set(2, 4); 
  box.display();
  cout << "The area occupied by the box is " << box.area() << "\n";
  // access members of derived class
  box.display_height();
  cout << "The volume of the box is " << box.volume() << "\n";
  return 0;
}
Compiling the altered program .cpp, you will get:
mercury[13]% CC 2d3d_a.cpp
"2d3d_a.cpp", line 24: error(3346): member "measure_2d::length" is inaccessible
int volume() { return length * width * height;}
^
"2d3d_a.cpp", line 24: error(3346): member "measure_2d::width" is inaccessible
int volume() { return length * width * height;}
^
2 errors detected in the compilation of "2d3d_a.cpp".
When the access specifier is private, all public and protected members of the base class become private members of the derived class. For example, let us keep everything in '2d3d.cpp' unchanged, except replacing the process specifier 'public' with 'private'. The new program is as below:
/************************************************************
Filename: 2d3d.cpp
Author: Zhiwei Wang
Date: July, 1997
Description: This program demonstrates how a derived class
(measure_3d) inherits the base class (measure_2d).
************************************************************/
#include <iostream>

using namespace std;

// base class definition
class measure_2d {
protected:
  int length, width;
public:
  void set (int a, int b) {length=a; width=b;}
  void display() { cout << "length=" << length << "\nwidth=" << width << "\n";}
  int area() { return length*width;}
};

// derived class definition
class measure_3d : private measure_2d {
  int height;
public:
  measure_3d (int a) {height=a;}
  void display_height() { cout << "height=" << height << "\n";}
  int volume() { return length * width * height;}
};

main(){
  measure_3d box(6);
  // error: cannot access members of base class
  box.set(2, 4); 
  box.display();
  cout << "The area occupied by the box is " << box.area() << "\n";
  // access members of derived class
  box.display_height();
  cout << "The volume of the box is " << box.volume() << "\n";
  return 0;
}
// End of Program
//****************************************************

The following is what we get when we compile the altered program:
mercury[18]% CC 2d3d_b.cpp
"2d3d_b.cpp", line 31: error(3346): function "measure_2d::set" is inaccessible
box.set(2, 4);
^
"2d3d_b.cpp", line 32: error(3346): function "measure_2d::display" is
inaccessible
box.display();
^
"2d3d_b.cpp", line 33: error(3346): function "measure_2d::area" is inaccessible
cout << "The area of the box is " << box.area() << "\n";
^
3 errors detected in the compilation of "2d3d_b.cpp".
When the access specifier is 'protected', all public and protected members of the base class become protected members of the derived class. Protected members are similar to private members in that they are not accessible to other non-members, but are different from private members when they are inherited: if the base class is inherited as protected or public, all protected members of the base class become protected members of the derived class.

7.18.14.3. Multiple Inheritance and Inheriting Multiple Base classes

A derived class can be used as a base class for another derived class, this is referred to as multiple inheritance. A class can inherit more than one classes, this is called inheriting multiple base classes. In the following program, class 'measure_2d' inherits 'measure_l' and 'measure_w', this is an example of inheriting multiple base classes; class 'measure_3d' inherits 'measure_2d', while 'measure_2d' itself is a derived class, this is an example of multiple inheritance.
/************************************************************
Filename: m_inherit.cpp
Author: Zhiwei Wang
Date: July, 1997
Description: This program gives examples of multiple 
inheritance and inheriting multiple classes.
demonstrates how a derived class
************************************************************/
#include <iostream>

using namespace std;

class measure_l { // base class
protected:
  int length;
public:
  void displayl() { cout << "length=" << length << "\n";}
};

class measure_w { // base class
protected:
  int width;
public:
  void displayw() { cout << "width=" << width << "\n";}
};

// measure_2d inherits two classes, an example of inheriting
// multiple classes
class measure_2d: public measure_l, public measure_w {
public:
  void set (int a, int b) {length=a; width=b;}
  void display() { cout << "length=" << length << "\nwidth=" << width << "\n";}
  int area() { return length*width;}
};


// measure_3d inherits a derived class, an example of
// multiple inheritance 
class measure_3d : public measure_2d {
  int height;
public:
  measure_3d (int a) {height=a;}
  void display_height() { cout << "height=" << height << "\n";}
  int volume() { return length * width * height;}
};

main(){
  measure_3d box(6);
  box.set(2, 4);
  box.display();
  cout << "The area of the box is " << box.area() << "\n";
  box.display_height();
  cout << "The volume of the box is " << box.volume() << "\n";
  return 0;
}
// End of Program
//***************************************************

7.18.15. ARRAYS OF OBJECTS

7.18.15.1 Arrays of Objects

An array is a collection of variables of the same data type. Its elements are referenced by the name of the array followed by a pair of square brackets with an index number in them. For example, the following statement declares an array of 100 integers.

int my_array[100];

The syntax for declaring an object array is the same as it is for any other data types, except that the forms to initialize an array are different according to how many parameters required by the constructor function.

7.18.15.2. Initialization without Parameters

The following example declares an array of object 'vector_1'.
/*********************************************************
Filename: vector1.cpp 
Description: 
**********************************************************/
#include <iostream>

using namespace std;

class Vector_1 {
  int x;
public:
  void set_x(int num) {x=num;}
  int get_x() {return x;}
};

main(){
  Vector_1 my_array[4];
  my_array[0].set_x(1);
  my_array[1].set_x(9);
  my_array[2].set_x(9);
  my_array[3].set_x(7);
  for (int i=0; i<4; i++) {
    cout << my_array[i].get_x() << endl;
  }
  return 0;
}
// End of Program
//****************************************************

7.18.15.3. Initialization with One Parameter

The following example is similar to the previous one but this time class vector_1 has a constructor function which requires one parameter. In this case, the array can be initialized by using a list of arameters.
/*********************************************************
Filename: vector1.cpp 
Description: 
**********************************************************/
#include <iostream>

using namespace std;

class Vector_1 {
  int x;
public:
  Vector_1 (int num) {x=num;} // constructor can not return a value.
  int get_x() {return x;}
};

main(){
  Vector_1 my_array[4] = {1, 9, 9, 7};
  for (int i=0; i<4; i++) {
    cout << my_array[i].get_x();
  }
  cout << endl;
  return 0;
}
// End of Program
//****************************************************

7.18.15.4. Initialization with More than One Parameter

The following example illustrates how to initialize an array of objects when the constuctor requires more than one parameters.
/*********************************************************
Filename: vector2.cpp
Date: July 24, 1997
Author: Zhiwei Wang
Student Number:
Description:
Declaring an array of objects.
**********************************************************/
#include <iostream>

using namespace std;

class Vector_2 {
  int x, y;
public:
  Vector_2 (int num1, int num2) {x=num1; y=num2;}
  int get_x() {return x;}
  int get_y() {return y;}
};

main(){
  Vector_2 my_array[2] = {vector_2(1, 9), vector_2(9, 7)};
  for (int i=0; i<2; i++) {
    cout << my_array[i].get_x() << my_array[i].get_y();
  }
  cout << endl;
  return 0;
}
// End of Program
//****************************************************


7.18.16. POINTERS TO OBJECTS

7.18.16.1. Pointers to Objects

The syntax to declare a pointer to an object is the same as that to declare a pointer to any other data types. Here is an example:

Example:

/************************************************************
Filename: pointer1.cpp
Author: Zhiwei Wang
Date: July, 1997
Description:
An example illustrating how to declare a pointer to object, 
and how to assign the address of an object to the pointer.
************************************************************/
#include <iostream>

using namespace std;

class vector {
  int x, y;
public:
  vector(){};
  vector(int a, int b) {x=a; y=b;}
  void display() {cout << "x=" << x << ", y=" << y << endl;}
};

main(){
  vector *p; // p is a pointer to vector
  vector f1(19, 97), f2(7, 12);
  p = &f1; // assigning the address of f1 to p
  cout << "vector 1 is: ";
  p->display(); // using an arrow to access the member function
  cout << "vector 2 is: ";
  f2.display();
  return 0;
}
In the preceding example, 'p' is a pointer to 'vector'. When you use a pointer (e.g. 'p') to access a member function, you use the arrow operator instead of a dot.

7.18.16.2. Pointer Arithmetic

Addition and substraction can be applied to pointers. When you declare an array of objects, the objects are stored in contiguous memory locations. If a pointer p is assigned the address of an array, then p points to the first item of the array, p+1 points to the second item, p+2 points to the third item, etc.

7.18.16.3. The 'this' Pointer

Every member function of an object has an implicit argument called this that is a pointer to the object. You can use the this pointer to refer to the object itself. This is especially useful when you overload an operator.

Example:

/************************************************************
Filename: this.cpp
Author: Zhiwei Wang
Date: July, 1997
Description:
An example showing how to use the 'this' pointer to refer to 
the calling object.
************************************************************/
#include <iostream>

using namespace std;

class vector {
  int x, y;
public:
  vector(){};
  vector(int a, int b) {x=a; y=b;}
  void display() {cout << "x=" << x << ", y=" << y << endl;}
  vector operator++();
};

vector vector::operator++() { 
  x++;
  y++;
  return *this;
}

main(){
  vector f1(19, 97);
  cout << "vector 1 is: ";
  f1.display();
  ++f1;
  cout << "After increment, it is: ";
  f1.display();
  return 0;
}
// End of Program

7.18.16.4. Pointers to Classes Members

C++ has a special type of pointer called 'a pointer to a class member'. A pointer to a class member provides an offset into an object at which the member can be found. Since a point to a class member is not a true pointer in the normal sense, to access a member, you use the special operators .* and ->* instead of using the operators . and ->.

When declaring pointers to members, you must specify the class and use the scope operator. The following is an example. Please pay attention to the syntax of each declaration.

/************************************************************
Filename: member.cpp
Author: Zhiwei Wang
Date: July, 1997
Description:
An example of using pointers to class members.
************************************************************/
#include <iostream>

using namespace std;

class vector {
public:
  int x, y;
  vector(){};
  vector(int a, int b) {x=a; y=b;}
  void display() {cout << "x=" << x << ", y=" << y << endl;}
};

main(){
  int vector::*d; // d is a pointer to data member.
  void (vector::*f)(); // f is a pointer to function member.
  vector f1(19, 97), f2(7, 12);
  vector *p; // p is a normal pointer to object.
  p = &f2; // assign the address of f2.
  d = &vector::x; // get offset of x.
  f = &vector::display; // get offset of display().
  // access data member.
  cout << "The first value of f1 is " << f1.*d << endl;
  // access function member.
  (f1.*f)();
  // access data member via pointer 'p'.
  cout << "The first value of f2 is " << p->*d << endl;
  // access function member via pointer 'p'.
  (p->*f)();
  return 0;
}
// End of Program

7.18.17. REFERENCES

In C, there are two ways to pass arguments to a function: 'call by value' and 'call by references'. C++ has a feature to do 'call by reference' automatically.

7.18.17.1. Pass Argument by Value

In general, when an argument is passed to a function, the value of the argument is copied to the 'formal parameter'. Any changes to the formal parameter has no effect on the argument. This is called 'pass by value'. The following program illustrates this. It attempts in vain to swap the value of two variables.
/************************************************************
Filename: swap1.cpp
Author: Zhiwei Wang
Date: July, 1997
Description:
This program illustrates that 'call by value' will not
change the value of the original arguments
************************************************************/
#include <iostream>

using namespace std;

void swap(int i, int j);

main(){
  int a = 1, b = 2;
  cout << " a = " << a << "\tb = " << b << endl;
  swap(a, b);
  cout << "After wrong swap:\n";
  // The value WILL NOT be changed.
  cout << " a = " << a << "\tb= " << b << endl;
  return 0;
}

void swap(int i, int j){
  int temp;
  temp = i;
  i = j;
  j = temp;
}
/* Output:
a = 1 b = 2
After wrong swap:
a = 1 b = 2
*/
// End of Program

7.18.17.2. Pass Argument by References

In order to change the value of an argument, we need to use the method called 'pass by references'. To do this, first, we need to locate the argument. This means that we should pass the address of the argument to the function. For example, when we call the function swap(), we should precede arguments 'a' and 'b' with the address operator '&':

swap(&a, &b);

Second, we need to tell the function that it is the contents, not the address, of the parameters that should be processed. This means that, in the definition of the function, we need to put the dereferencing operator '*' in front of the parameters:

void swap(int *i, int *j){
  int temp;
  temp = *i;
  *i = *j;
  *j = temp;
}
Accordingly, the prototype of the function should be changed to:

void swap(int *i, int *j);

The following is the revised program and its out put.

/************************************************************
Filename: swap2.cpp
Author: Zhiwei Wang
Date: July, 1997
Description:
This program illustrates that 'call by reference' will
change the value of the original arguments
************************************************************/
#include <iostream>
using namespace std;

void swap(int *i, int *j);

main(){
  int a = 1, b = 2;
  cout << " a = " << a << "\tb = " << b << endl;
  swap(&a, &b);
  cout << "After swap:\n";
  cout << " a = " << a << "\tb = " << b << endl;
  return 0;
}

void swap(int *i, int *j){
  int temp;
  temp = *i;
  *i = *j;
  *j = temp;
}
/* Output:
a = 1 b = 2
After swap:
a = 2 b = 1
*/
// End of Program
This time, the program gives the desired output.

7.18.17.3 Reference Arguments

C++ has a feature called 'reference arguments'. The use of reference arguments will automatically involve the 'pass by reference' mechanism. When you call the function, you don't need to use '&' to explicitly pass the address; When you write the body of the function, you don't need the '*' sign neither. The only thing you need to do is to precede the argument with a '&' sign in the header of the function definition. Of course the prototype should be changed accordingly. The following is the 'reference arguments' version of the two preceding programs.
/************************************************************
Filename: swap2.cpp
Author: Zhiwei Wang
Date: July, 1997
Description:
This program illustrates that 'call by reference' will
change the value of the original arguments
************************************************************/
#include <iostream>

using namespace std;

void swap(int &i, int &j);

main(){
  int a = 1, b = 2;
  cout << " a = " << a << "\tb = " << b << endl;
  swap(a, b);
  cout << "After swap:\n";
  cout << " a = " << a << "\tb = " << b << endl;
  return 0;
}

void swap(int &i, int &j){
  int temp;
  temp = i;
  i = j;
  j = temp;
}

7.18.18. VIRTUAL FUNCTIONS

7.18.18.1. Polymorphic Classes and Virtual Functions

polymorphic class is a class that includes a virtual function. A virtual function is a special member function that is declared as virtual in a base class and re-defined (called overridden) in derived classes. In the following example, 'person' is a polymorphic class, which includes a virtual function greeting(). The class 'chinese' inherits 'person' and overrides the virtual function greeting().

Example

class person {
public:
  virtual void greeting() { cout << "Hello!\n";}
};

class chinese : public person { // greeting() is re-defined
public:
  void greeting() { cout << "Ni Hao! I am a Chinese\n";}
};

7.18.18.2. Virtual Function Invocation

Virtual functions are used to accomplish run-time polymorphism, which means using same interface for different methods. To do this, you need to invoke virtual functions through a pointer to a particular class type. The program below is an illustration.
/************************************************************
Filename: virtual.cpp
Author: Zhiwei Wang
Date: July, 1997
Description: This program illustrates how to use virtual functions.
************************************************************/
#include <iostream>

using namespace std;

class person {
public:
  virtual void greeting() { cout << "Hello!\n";}
};

class woman : public person { // greeting() is not overridden

}

class man : public person { // greeting() is overridden
public:
  void greeting() { cout << "Hello! Lady first.\n";}
};

main(){
  person *p, b;
  woman d1;
  man d2;
  
  p = &b;
  p->greeting();
  p = &d1;
  p->greeting();
  p = &d2;
  p->greeting();
  return 0;
}
/* Output:
Hello!
Hello!
Hello! Lady first.
*/

7.18.18.3. Pure Virtual Functions and Abstract Classes

pure virtual function is a virtual function that has no definition in the base class. The format to declare a pure virtual function is:

virtual type function_name(parameter-list) = 0;

When a virtual function is made pure, all derived classes must provide their own definition, otherwise a compile-time error will occur.

An abstract class is a class that contains pure virtual function(s). It is used as a foundation for derived classes but cannot be used directly to create objects. However, pointers to an abstract class are legal. This allows run-time polymorphism.

/************************************************************
Filename: abstract.cpp
Author: Zhiwei Wang
Date: July, 1997
Description: A simple example of using abstract class
An abstract class is a class that contains pure 
virtual function(s). A pure virtual function is
a virtual function that has no definition
in the base class. An abstract class cannot be used 
directly to create objects. However, pointers to an 
abstract class are allowed.
************************************************************/
#include <iostream>

using namespace std;

class person { // 'person' is an abstract class
public:
  virtual void greeting() = 0; // 'greeting()' is now a pure 
                               //virtual function
};

class woman : public person { // greeting() must be overridden
public:
  void greeting() { cout << "Hello!\n";}
};

class man : public person { 
public:
  void greeting() { cout << "Hello! Lady first.\n";}
};

main(){
  // Abstract class cannot be used to create object 'b'.
  // so comments out the following line
  // person *p, b; 
  
  // However, abstract class can be used to create a pointer 
  // to it.

  person *p; 
  woman d1;
  man d2;
  p = &d1;
  p->greeting();
  p = &d2;
  p->greeting();
  return 0;
}
/* Output:
Hello! 
Hello! Lady first.
*/
// End of Program

7.18.19. TEMPLATES

7.18.19.1. Generic Functions

A generic function (also called template function) is a function that defines a general algorithm applicable to various types of data. A generic function is defined with the keyword template:
template <class Ttype> ret-type func-name (parameter list) {
  // body of function
}
Ttype is a placeholder for the data type used by the function. In the following example, the function 'swap()' is a generic function that can be used to swap two numbers, characters, etc.
/************************************************************
Filename: swap.cpp
Description: A simple example of using template
************************************************************/
#include <iostream>

using namespace std;

// a function template
template <class Whatever> void swap(Whatever &a, Whatever &b) {
  Whatever temp;
  temp = a;
  a = b;
  b = temp;
}

main(){
  cout << "swap() can be used to swap two integers:\n";
  int i=1, j=2;
  cout << "Original:\ti=" << i << ";\tj=" << j << ".\n";
  swap(i, j);
  cout << "Swapped:\ti=" << i << ";\tj=" << j << ".\n";
  cout << "swap() can be used to swap two real numbers:\n";
  float num1=1.2, num2=3.4;
  cout << "Original:\tnum1=" << num1 << ";\tnum2=" << num2 << ".\n";
  swap(num1, num2);
  cout << "Swapped:\tnum1=" << num1 << ";\tnum2=" << num2 << ".\n";
  cout << "swap() can be used to swap two characters:\n";
  char ch1='a', ch2='b';
  cout << "Original:\tch1=" << ch1 << ";\tch2=" << ch2 << ".\n";
  swap(ch1, ch2);
  cout << "Swapped:\tch1=" << ch1 << ";\tch2=" << ch2 << ".\n";
  return 0;
}
/* Output:
swap() can be used to swap two integers:
Original: i=1; j=2.
Swapped: i=2; j=1.
swap() can be used to swap two real numbers:
Original: num1=1.2; num2=3.4.
Swapped: num1=3.4; num2=1.2.
swap() can be used to swap two characters:
Original: ch1=a; ch2=b.
Swapped: ch1=b; ch2=a.
*/
// End of program

7.18.19.2. Generic Classes

The general form to declare a generic class is:
template <class Ttype> class class-name{
   ...
}
Once a generic class is declared, you create an object of that class using the following format:
class-name <type> variable-name;

Example:

/************************************************************
Filename: queue.cpp
Author: Zhiwei Wang
Date: July, 1997
Description:

This program gives an example of using generic classes 
and generic functions. The program implements a queue 
on an array. The class 'queue' is a generic class. 
You can use if for a queue of integers, 'float's, characters, etc. 
The member function 'enqueue()' adds an item to the queue, 
the function 'dequeue()' takes an item from the queue, 
and the function 'showqueue()' display the queue.
'menu' is a generic function. It can be used for a 
queue of integer items, 'float' items, 'char' items, etc.
************************************************************/
#include <iostream>

using namespace std;

const int SIZE = 100;

template <class QType> class queue {
  QType q[SIZE];
  int head, tail, count;
public:
  queue();
  QType item;
  void enqueue(QType i);
  QType dequeue();
  void showqueue();
};

template <class QType> queue<QType>::queue() {
  head = 0;
  tail = 0;
  count = 0;
  cout << "Queue created.\n";
}

template <class QType> void queue<QType>::enqueue(QType i) {
  if (count == SIZE) {
    cout << "Queue is full.\n";
    return;
  }
  q[tail++] = i;
  count++;
  tail %= SIZE;
}

template <class QType> QType queue<QType>::dequeue() {
  if (count == 0) {
    cout << "Queue is empty.\n";
    return 0;
  }
  int temp;
  temp = head;
  head = (head+1)%SIZE;
  count--;
  return q[temp];
}

template <class QType> void queue<QType>::showqueue() {
  if (count == 0) {
    cout << "Queue is empty.\n";
    return;
  }
  int temp = head;
  for (int i=0; i<count; i++) {
    cout << q[temp] << ";";
    temp = (temp+1)%SIZE;
  }
}

template <class QT> void menu(QT q) {
  int choice = 0;
  while (choice != 4) {
    cout << "What do you want to do? Type number 1, 2, 3, or 4.\n";
    cout << "1. add an item to the queue.\n";
    cout << "2. take an item from the queue.\n";
    cout << "3. display the queue.\n";
    cout << "4. quit.\n";
    cin >> choice;
    if (choice == 4) return;
    switch (choice) {
      case 1:
        cout << "Enter an item, please: ";
        cin >> q.item;
        q.enqueue(q.item);
        cout << "\nItem enterred.\n";
        break;
      case 2:
        cout << "The item to be served is: " << q.dequeue() << endl;
        cout << "\nDone.\n";
        break;
      case 3:
        cout << "The queue is:\n";
        q.showqueue();
        break;
      default:
        cout << "Please type 1, 2, 3, or 4\n";
    }
  }
}

main() {
  int i=0;
  while (i != 4) {
    cout << "You can build a queue of data type int, float, or char\n";
    cout << "1. integer\t2. float\t3. char \t4. quit\n";
    cout << "Enter your choice: (Type 1, 2, 3, or 4)\n";
    cin >> i;
    if (i == 4) return 0;
    switch (i) {
      case 1:
        queue<int> iq;
        menu(iq);
        break;
      case 2:
        queue<float> fq;
        menu(fq);
        break;
      case 3:
        queue<char> cq;
        menu(cq);
        break;
      default:
        cout << "You have to choose 1, 2, 3, or 4.\n";
    }
  }
  return 0;
}
// End of Program

7.18.19.3. Examples with More than One Generic Data Types

You can use a comma-separated list for more than one generic data types, as shown in the example below:
/************************************************************
Filename: moretype.cpp
Author: Zhiwei Wang
Date: July, 1997
Description:
An example of multiple generic data types
************************************************************/
#include <iostream>

using namespace std;

template <class Type1, class Type2, class Type3> class record {
  Type1 name;
  Type2 value;
  Type3 info;
public:
  record(Type1 a, Type2 b, Type3 c) {name=a; value=b; info=c;}
  void display();
};

template <class Type1, class Type2, class Type3>
void record<Type1, Type2, Type3>::display() {
  cout << "Item1:\t" << name << endl;
  cout << "Item2:\t" << value << endl;
  cout << "Item3:\t" << info << endl;
}

main() {
  record<char*, int, double> person("John", 32, 3245.34);
  person.display();
  cout << endl;
  record<int, char, char*> note(3, 'A', "Call me");
  note.display();
  return 0;
}
/* Output:
Item1: John
Item2: 32
Item3: 3245.34
Item1: 3
Item2: A
Item3: Call me
*/
// End of Program

7.18.20. Exceptions

Error codes returned from procedures do not convey much information to 
the calling procedure. It is usually a number that indicates the cause 
of failure. But in many situations, it would be very helpful if more 
informaiton about the cause of failure is available to the caller. 
A simple error code cannot fulfill this goal. A popular alternative to 
returning error code is to raise exceptions. Exceptions are run-time 
anomalies that a program may detect. They indicate an abnormal conditon 
that should not be encountered.

7.18.20.1. The throw Statement

In C++, an exception is raised using a throw expression, which is composed of the keyword
throw followed by an expression.

Example:
NodeData Queue::Dequeue()
{
NodeData    Temp;

//  Since "End of File" is an expected condition for a Queue, we
//  must do something to signal EOF. So, we use the Catch/Throw
//  mechanism in C++, and modify the exception name to reflect that
//  this is a Queue.

    if (IsEmpty())
        throw EOQ();

    ToHead();
    Temp = Read();
    Delete();

    return Temp;
}

In the previous example, EOQ is a class defined inside class Queue by the following statement:
class        EOQ {};
The expression followed the throw keyword is an invocation of the constructor of EOQ.  This invocation creates an object of EOQ, which is thrown by the throw statement.

7.18.20.2. The try Statement

The code that can throw exceptions should be enclosed in a try block. Following the try block is a list of catch clauses.
Example

try {
  Current = SearchQ.Dequeue();
  //  If we get here, we successfully dequeued a state from
  //  the Search Queue, so we must decrement the size of the
  //  Leaf List.
  LeafList--;
}
catch (PriorityQueue::EOQ) {
  NodesRemaining = FALSE;
  cout << "\nThere is no solution to this Puzzle!" << endl;
  break;
}

The general forms of try and catch statement are shown as follows:
try {
  // try block
}
catch (type1 arg) {
  // catch block
}
catch (type2 arg) {
  //catch block
}
.
.
.
catch (typen arg) {
  // catch block
}

When an exception is thrown, if its data type matchs the data type specified by a catch statement, the exception will be
caught, and the corresponding catch statement will be executed. All the other catch statements will be bypassed.
When an exception is caught, arg will receive it value.

Example:
#include <iostream>

using namespace std;
main() {

  try {
    cout << "begin" << endl;
    throw 100;
    cout << "this will not be shown" << endl;
  }
  catch (int i) {
    cout << "Caught an exceptionm, the value is: ";
    cout << i << endl;
  }
  cout << "The End" << endl;
}

output:
begin
Caught an exceptionm, the value is: 100
The End

If you change the data type in the catch statement from int to a different type,
then the exception will not be caught and abnormal termination of the program will occur.

Example:
#include <iostream>

using namespace std;
void Ehandler(int i) {
  try {
    if (i==0)
      throw "Value is zero..";
    else
      throw i;
  }
  catch (int e) {
    cout << "Caught: " << e << endl;
  }
  catch (char *str) {
     cout << "Caught: " << str << endl;
  }
}

main() {
  cout << "Begin" << endl;

  Ehandler(1);
  Ehandler(0);
  Ehandler(2);

  return 0;
}

output:
Begin
Caught: 1
Caught: Value is zero..
Caught: 2
 

7.18.20.3. Caught all Exceptions

Example: Using Multiple catch Statements
#include <iostream>

using namespace std;

void Ehandler(int i) {
  try {
    switch (i) {
      case 0:
         throw "Value is zero.";
         break;
      case 1:
         throw 'a';
         break;
      case 2:
         throw 3.14;
        break;
      default:
         throw i;
      }
   }
   catch (int e) {
    cout << "Caught: " << e << endl;
  }
  catch (char * e) {
     cout << "Caught: " << e << endl;
  }
  catch (...) {
    cout << "Caught one." << endl;
  }
}

main() {
  cout << "Begin" << endl;

  Ehandler(0);
  Ehandler(1);
  Ehandler(2);
  Ehandler(3);
  return 0;
}
Output:
Begin
Caught: Value is zero.
Caught one.
Caught one.
Caught: 3
 

7.18.20.4. Restricting Exceptions

If necessary, you can restrict the types of exceptions a function can throw. The general form of doing this is:
ret-type func-name(arg-list throw (type list) {
  // function body
}

Example
#include <iostream>

using namespace std;

void Ehandler(int i) throw (int, char, float, char *) {
     switch (i) {
      case 0:
         throw "Value is zero.";
         break;
      case 1:
         throw 'a';
         break;
      case 2:
         throw 3.14;
        break;
      default:
         throw i;
      }
}

main() {
  cout << "Begin\n";
  try {
    Ehandler(0);
  } catch (int i) {
    cout << "Caught an integer\n";
  } catch (char c) {
    cout << "Caught a character\n";
  } catch (float n) {
    cout << "Caught a float\n";
  }  catch (char * str) {
    cout << "Caught a string\n";
  }
  cout << "end" << endl;
}

Throwing any other types of exceptions will cause abnormal termination.  If you don't want to throw any exceptions, use an empty list.

Example
#include <iostream>

using namespace std;
// This function can throw no exception
void Ehandler(int i) throw () {
     switch (i) {
      case 0:
         throw "Value is zero.";
         break;
      case 1:
         throw 'a';
         break;
      case 2:
         throw 3.14;
        break;
      default:
         throw i;
      }
}

Note that the restriction applies only to the types of exceptions that are thrown back to the calling function.  Within a function, a try block can throw any exception as long as it is caught within the function.

#include <iostream>
using namespace std;
void Ehandler(int i) throw () {
       try {
         switch (i) {
           case 0:
              throw "Value is zero.";
              break;
           case 1:
              throw 'a';
              break;
           case 2:
              throw 3,14;
             break;
           default:
              throw i;
           }
       }
       catch (int e) {
         cout << "Caught: " << e << endl;
       }
       catch (char * e) {
         cout << "Caught: " << e << endl;
       }
       catch (...) {
         cout << "Caught one." << endl;
       }
}
 

7.18.20.5. Rethrowing an Exception

An exception can be re-thrown from within a catch block by calling throw with no exception.

Example:
#include <iostream>
using namespace std;
void Ehandler(){
  try {
    throw "Hello";
  }
  catch (char *) {
    cout << "Caught char * inside Ehandler.\n";
    throw;  // rethrow char *
  }
}

main() {
  cout << "Begin\n";
  try {
    Ehandler();
  }
  catch (char * ) {
    cout << "Caught char * inside main\n";
  }
  cout << "end" << endl;
}

Exception handling is designed to provide a structured means by which your program can handle abnormal events.

Example:
#include <iostream>


using namespace std;


void divide(double a, double b);

main() {
  double i, j;
  do {
    cout << "Enter numerator (0 to stop): ";
    cin >> i;
    cout << "Enter denominator: ";
    cin >> j;
    divide(i, j);
  } while (i != 0);
}

void divide(double a, double b) {
  try {
    if (!b) throw b;
    cout << "Result: " << a/b << endl;
  } catch (double b) {
    cout << "Can't divide by zero.\n";
  }
}