Engineering Problem Solving
with C++, Etter/Ingber
Chapter 10
Additional Topics in Programming
with Classes
Engineering Problem Solving with
C++, Second Edition. J. Ingber
1
Programming with Classes
Introduction to Generic Programming
Recursion
Class Templates
Inheritance
virtual Methods
Engineering Problem Solving with
C++, Second Edition. J. Ingber
2
function templates
overloading operators
friend functions
INTRODUCTION TO GENERIC
PROGRAMMING
Engineering Problem Solving with
C++, Second Edition. J. Ingber
3
Generic Programming
Generic programming supports the
implementation of a type independent algorithm.
Type independent algorithms can be defined in
C++ using the keyword template, and at least
one parameter.
The compiler generates a unique instance of the
template for each specified type.
Engineering Problem Solving with
C++, Second Edition. J. Ingber
4
Function Templates
A function template is a parameterized function definition.
Syntax:
template<typename identifier [,typename identifier,…] >
return-type function name(parameter list)
{ …}
Example:
template<typename Dtype>
void swapTwo(Dtype& a, Dtype& b)
{
Dtype temp = a;
a = b;
b = temp;
}
Engineering Problem Solving with
C++, Second Edition. J. Ingber
5
Instantiating Templates
Example:
template<typename Dtype& a, Dtype& b>
void swapTwo(Dtype& a, Dtype& b); //prototype
…
int main()
Output:
{
5.7, 1.0
double x(1.0), y(5.7);
on
char ch1('n'), ch2('o');
swapTwo(x,y);
swapTwo(ch1, ch2);
cout << x << ',' << y << endl
<< ch1 << ch2 << endl;
...
Engineering Problem Solving with
C++, Second Edition. J. Ingber
6
Overloading Operators
Allows a programmer defined data type to be used in the
same way that a predefined data type is used.
Definitions for overloaded operators are included in the
class definition.
– keyword operator is used followed by the operator.
– the operator must be one of the predefined C++
operators.
– Only predefined operators may be overloaded.
All predefined operators except( .
::
.*
?:
sizeof) may be overloaded.
Engineering Problem Solving with
C++, Second Edition. J. Ingber
7
Complex Number Class
A complex number is a number that has two components;
the real component and the imaginary component.
a + bi
Arithmetic is defined as follows:
(a + bi) + (c + di) = (a + c) + (b + d)i
(a + bi) - (c + di) = (a - c) + (b - d)i
(a + bi) * (c + di) = (ac - bd) + (ad + bc)i
(a + bi) / (c + di) = (ac + bd) / (c**2+d**2) +
[ (bc -ad) /(c**2+d**2)]i
Engineering Problem Solving with
C++, Second Edition. J. Ingber
8
Class Declaration
class complex
{
public:
complex();
complex(double,double);
void print(ostream&);
void input(istream&);
complex operator+(complex) const;
complex operator-(complex) const;
complex operator*(complex) const;
complex operator/(complex) const;
private:
double real, imag;
};
Engineering Problem Solving with
C++, Second Edition. J. Ingber
9
Implementation - constructors
complex::complex()
{
//default constructor
real=imag=0;
}
complex :: complex(double r, double im)
{
real = r;
imag = im;
}
Engineering Problem Solving with
C++, Second Edition. J. Ingber
10
Implementation –
Overloaded Operators
complex complex::operator+(complex c) const
{
complex temp;
temp.real = real + c.real;
temp.imag = imag + c.imag;
return temp;
}
Engineering Problem Solving with
C++, Second Edition. J. Ingber
11
Implementation - Continued
complex complex::operator/(complex c) const
{
complex temp;
temp.real = (real*c.real + imag*c.imag)/
( pow(c.real,2) + pow(imag,2) );
temp.imag = (imag*c.real - real*c.imag)/
( pow(c.real,2) + pow(imag,2) );
return temp;
}
Engineering Problem Solving with
C++, Second Edition. J. Ingber
12
Practice! – Implement the * operator
(a + bi) * (c + di) = (ac - bd) + (ad + bc)i
complex complex::operator*(complex c) const
{
complex temp;
temp.real = real*c.real – imag*c.imag;
temp.imag = real*c.imag + imag*c.real;
return temp;
}
Engineering Problem Solving with
C++, Second Edition. J. Ingber
13
Test Program
complex c1, c2, c3; //declare three complex variables
c1.input(cin);
c2.input(cin);
//test addition
c3 = c1 + c2;
// using overloaded operator +
cout << endl << "c1 + c2 is ";
c3.print(cout);
//test division
c3 = c1 / c2;
// using overloaded operator /
cout << endl << "c1 / c2 is ";
c3.print(cout);
cout << endl;
Engineering Problem Solving with
C++, Second Edition. J. Ingber
14
Sample Output
Using the following input:
4.4 1.5
3.5 -2.5
The expected output from our test program will
be:
c1 + c2 is 7.9 + -1i
c1 / c2 is 0.62973 + 0.878378i
Engineering Problem Solving with
C++, Second Edition. J. Ingber
15
Variations
Overloading Operators as:
– member functions
– friend functions
– top level functions
Engineering Problem Solving with
C++, Second Edition. J. Ingber
16
Member Functions
binary operators (ie +,-, * )
– member function requires one argument
– the left hand operand is the object invoking the operator
unary operator
– member function requires no arguments
– assumed to be prefix version of operators ++ and –
– postfix versions of operators ++ and – are not covered
in this text
Disadvantages
– first argument must be an object of the class
Engineering Problem Solving with
C++, Second Edition. J. Ingber
17
Example – class complex
In complex.h
– complex operator +(complex c);
operator + implemented in complex.cpp
In a client program
complex a, b, c;
c = a + b;
//a is calling object, b is argument
c = a + 54.3; //OK, constructor is called to
// convert 54.3 to complex object
c = 54.3 + a // is not allowed
Engineering Problem Solving with
C++, Second Edition. J. Ingber
18
Friend Functions
binary operators
– friend function requires two arguments
unary operator
– friend function requires one argument
Disadvantage
– A friend function is NOT a member function
– Friend functions violate a strict interpretation of
object oriented principals (implementation is hidden)
– Recommended for operator overloading only
Engineering Problem Solving with
C++, Second Edition. J. Ingber
19
Example:
In class definition:
friend complex operator +(complex c1, complex c2);
In class implementation:
complex operator +(complex c1, complex c2)
{ complex temp;
temp.real = c1.real + c2.real;
temp.imag = c1.imag + c2.imag;
return temp;
}
In client program:
complex cA, cB, cC;
cC = cA+cB;
cC = 54.3 + cB; //this is ok, when + is a friend function
Engineering Problem Solving with
C++, Second Edition. J. Ingber
20
Class Declaration
class complex
{ public:
complex();
complex(double,double);
friend ostream& operator <<(ostream&, complex);
friend istream& operator >>(istream&, complex&);
complex operator+(complex) const;
complex operator-(complex) const;
complex operator*(complex) const;
complex operator/(complex) const;
private:
double real, imag;
};
Engineering Problem Solving with
C++, Second Edition. J. Ingber
21
Implementation
ostream& operator <<(ostream& os, complex r)
{ os << r.real << “ + “ << r.imag << ‘i’;
return os;
}
istream& operator >> (istream& is, complex& r)
{ is >> r.real >> r.imag;
return is;
}
Engineering Problem Solving with
C++, Second Edition. J. Ingber
22
Error Checking on input operator
If your input fails because of incorrect format, your
function should mark the state of the istream as bad
is.clear(ios::badbit | is.rdstate() )
clear resets entire error state to zero
clear(ios::badbit) clears all and sets badbit
is.rdstate() returns the previous state of all bits
Statement sets the bit vector to the OR of badbit with
previous state
Engineering Problem Solving with
C++, Second Edition. J. Ingber
23
Top Level Functions
binary operators
– top level function requires two arguments
unary operator
– top level function requires one argument
Disadvantage:
– Top level functions do not have access to
private data members. Function must use
accessor functions.
Engineering Problem Solving with
C++, Second Edition. J. Ingber
24
Example of top level function
Function prototype in client program.
complex operator +(complex c1, complex c2);
Implemention in client program.
complex operator +(complex c1, complex c2)
{ return complex(c1.get_real() + c2.get_real(),
c1.get_imag() + c2.get_imag());
}
Use in client program.
complex cA, cB, cC;
cC = cA + cB;
cC = 54.3 + cA; //this is ok, when + is a top level
//function
Engineering Problem Solving with
C++, Second Edition. J. Ingber
25
factorial function
binary trees
RECURSION
Engineering Problem Solving with
C++, Second Edition. J. Ingber
26
Recursion
Recursion is a powerful tool for solving certain
classes of problems where:
– the problem solution can be expressed in terms of the
solution to a similar, yet smaller problem.
Redefinition of the problem continues in an
iterative nature until:
– a unique solution to a small version of the problem is
found.
This unique solution is then used, in a reverse
iterative nature until:
– the solution to the original problem is returned.
Engineering Problem Solving with
C++, Second Edition. J. Ingber
27
Recursion
Programming languages that support recursion
allow functions to call themselves.
Each time a function calls itself, the function is
making a recursive function call.
Each time a function calls itself recursively,
information is pushed onto the runtime stack.
Each time a recursive function calls returns,
information is popped off the stack. The return
value, along with the information on the stack, is
used to solve the next iteration of the problem
Engineering Problem Solving with
C++, Second Edition. J. Ingber
28
Recursive Functions
A recursive function requires two blocks:
– a block that defines a terminating condition, or
return point, where a unique solution to a
smaller version of the problem is returned.
– a recursive block that reduces the problem
solution to a similar but smaller version of the
problem.
Engineering Problem Solving with
C++, Second Edition. J. Ingber
29
Example: Recursive Definition of
Factorial Function
f (n) n! {1 : n 0, n * f (n 1) : n 1}
f(0) = 1 Unique solution.
f(n) = n*f(n-1) Recursive definition.
Thus, f(n) can be determined for all integer values of n>=0;
Engineering Problem Solving with
C++, Second Edition. J. Ingber
30
Recursive Factorial Function
long factorialR(int n)
{
/*Termination condition */
if(n==0)
{
return 1; //unique solution
}
/*Recursive block – reduce problem */
return n*factorialR(n-1);
}
Engineering Problem Solving with
C++, Second Edition. J. Ingber
31
Binary Tree Abstraction
• A binary tree maintains two links between nodes.
• The links are referred to as the left child and the right child.
• The first node is called the root.
• Each child(left and right) may serve as the root to a subtree.
• A node without children is referred to as a leaf node.
32
Diagram
pointer to root
*
+
leaf node
8
2
4
33
leaf node
Implementation
• A BinaryTree class with one attribute:
a pointer (node*) to the root of a binary tree.
34
BinaryTree class
Methods:
insert()
delete()
print()
inOrder(), preOrder(), postOrder()
Implementation of insert:
Insert into empty BinaryTree establishes the root.
Each subsequent node is inserted in order:
values less than root are placed in the root’s left subtree
values greater than root are placed in the root’s right subtree
35
preOrder Traverse
display node
visit left child
visit right child
*+248
36
postOrder Traverse
visit left child
visit right child
display node
24+8*
37
inOrder Traverse
Visit left child
Display node
Visit right child
2+4*8
38
BinaryTree Class
Recursive Methods:
– print(), insert(), clear()
Recursive methods are overloaded.
public version is non-recursive.
public version is called once.
public version calls private recursive
version.
Recursive version calls itself.
binary tree
CLASS TEMPLATES
Engineering Problem Solving with
C++, Second Edition. J. Ingber
40
class Templates
A binary tree is an ordered collection of nodes.
Each node has a data value, a right child and a
left child.
The data type of the right and left child is
node*.
The data type of the node value is parameterized
to form a class template.
The binary tree template also parameterizes the
node type..
Engineering Problem Solving with
C++, Second Edition. J. Ingber
41
BinaryTree Declaration
#include "node.h"
template<typename T>
class BinaryTree {
public:
BinaryTree():root(0){} //inline
bool empty(){return root==0;} //inline
void clear(){ if(root) {clear(root); root=0;} } //inline, non recursive
void insert(const T& );
//…
private:
void clear(node<T>*); //recursive
node<T>* root; //node is a class template
};
42
Implementation of BinaryTree class
//clear
template<typename T>
void BinaryTree<T>::clear(node<T> *pt)
//method called from private clear()
{
if(pt){ clear(pt->getLeft()); clear(pt->getRight()); delete pt;}
}
//insert
template<typename T>
void BinaryTree<T>::insert(const T& value)
{
if(!root) root = new node<T> (value); //must qualify
else root->insertValue(value);
//member of node
}
43
insertValue: member of node class
template<typename valType>
void node<valType>::insertValue(const valType& val){
if(val < data){
if(!left) left=new node(val);
//don’t need to quality
else left->insertValue(val);
}
else {
if(!right) right = new node(val);
else right->insertValue(val);
}
}
44
Sample Run
int main()
{
BinaryTree<int> tree;
if(tree.empty()) cout << "empty" << endl;
tree.insert(10);
if(tree.empty()) cout << "empty" << endl;
else cout << "not empty\n";
tree.clear();
if(tree.empty()) cout << "empty" << endl;
else cout << "not empty\n";
return 0;
}
45
output:
empty
not empty
empty
Suggestions for Writing templates
• Get a non-template version working first.
• Establish a good set of test cases.
• Measure performance and tune.
• Review implementation:
• Which types should be parameterized?
• Convert non-parameterized version into
template.
• Test template against established test cases.
46
INHERITANCE
Engineering Problem Solving with
C++, Second Edition. J. Ingber
47
Inheritance
Inheritance is a means by which one class acquires
the properties--both attributes and methods--of
another class.
When this occurs, the class being inherited from is
called the base class.
The class that inherits is called the derived class.
Engineering Problem Solving with
C++, Second Edition. J. Ingber
48
C++ and Public Inheritance
The private members of a base class are only
accessible to the base class methods.
The public members of the base class, are accessible
to the derived class and to clients of the derived class.
The protected members of the base class, are
accessible to members of the derived class, but are not
accessible to clients of the base class or clients of the
derived class.
Engineering Problem Solving with
C++, Second Edition. J. Ingber
49
UML Inheritance Diagram:
Rectangle
Point
Square
•A Rectangle has a Point.
•Square is a Rectangle.
Engineering Problem Solving with
C++, Second Edition. J. Ingber
50
Example – Base Class Definition
class Rectangle
{
private:
double width, height;
Point origin;
public:
Rectangle();
Rectangle(double w, double h, double x, double y);
//Accessors
//Mutators
};
Engineering Problem Solving with
C++, Second Edition. J. Ingber
51
Example: Derived Class
class Square : public Rectangle
{
public:
Square();
Square(const Point&, double s);
double getSide() const;
void setSide(double);
};
Engineering Problem Solving with
C++, Second Edition. J. Ingber
52
Constructors and Inheritance
When an object is created, its constructor is called
to build and initialize the attributes.
With inheritance, the invocation of constructors
starts with the most base class at the root of the
class hierarchy and moves down the path to the
derived classes constructor.
If a class is to use the parameterized constructor of
a base class, it must explicitly invoke it.
Engineering Problem Solving with
C++, Second Edition. J. Ingber
53
Implementation of Constructor
Rectangle::Rectangle(double w, double h,
double x, double y) : origin(x,y)
{
}
Square::Square(const Point&p,
double side) : Rectangle(side,side)
{
}
Engineering Problem Solving with
C++, Second Edition. J. Ingber
54
VIRTUAL METHODS
Engineering Problem Solving with
C++, Second Edition. J. Ingber
55
Methods
All methods are, by default, non-virtual methods.
Binding of method call is determined by static
type of calling object.
Example:
Rectangle r1;
Square s1;
r1 = s1;
r1.print(cout);//Calls print() defined in
//Rectangle
Engineering Problem Solving with
C++, Second Edition. J. Ingber
56
virtual Methods
If a method is defined to be virtual, and
pointers or references to objects are used,
then dynamic binding is supported.
Example:
Rectangle *rPtr;
Point p2;
Square s1 = s1(p2,5);
rPtr = &s1;
rPtr->print(cout);//Calls print() defined in
//Square
Engineering Problem Solving with
C++, Second Edition. J. Ingber
57
© Copyright 2026 Paperzz