Object-Oriented Programming

advertisement
1
Object-Oriented Programming
1-Basic elements of programs
2-Object-Oriented Versus Procedural Programming
3-C++ Compilation and Linking
4-Objects
5-Stages of Program Development
6-Code: Some C++ Program Anatomy
7-Bugs
8-Tokens
9-Identifiers
10-Keywords
11-Constants
12-Operators
13-Separators
14-White space
15-Programming Style
16-Built-in Data Types
17-unsigned keyword
18-Declaration
19-Definition
20-Initialization
21-Addresses
22- = Assignment Operator
23-sizeof
24-Debugger
25-Arithmetic Expressions
26-Precedence and Associativity
27-Expressions with Multiple Data Types
28-Ranking for Conversions
29-Narrowing Conversions
30-Explicit Conversions or Cast Operators
31-Functions
32-Code: Procedural Voltage Drop Calculation
33-void Data Type
34-Code: Using a Function Return Value as an Argument in a
Function Call
35-Overloaded Functions
36-Code: Procedural Voltage Drop Calculations with Overloaded
Functions
37-Default Argument Values
38-Code: Procedural Voltage Drop Calculations with Default
Arguments
39-Scope (Visibility) of an Identifier
40-Memory Storage Classes
41-Linkage of an Identifier
42-Lifetime of a Variable or Object
43- :: Global Scope Resolution Operator
44- static Keyword
45- extern Keyword
46-Code: Illustrating Scope and Lifetime
47-const Keyword
48-Code: Illustrating const Variables and Internal Linkage
49-Algorithms
50-Boolean Variables
5
5
5
6
6
6
7
7
7
8
8
8
9
9
9
10
11
11
11
11
11
12
12
13
13
14
14
15
15
15
16
16
18
18
18
19
20
20
21
21
21
21
22
22
22
22
23
24
25
25
2
51-Boolean Expressions
52-Boolean Expression Operators
53-Pseudo-code
54-if-else Branching Statement
55-exit() Standard Library Function
56-assert() Standard Library Function
57-for Iteration Statement
58-while Iteration Statement
59-Forever Loops and break Statement
60-continue Statement
61-Conditional Compilation with Preprocessor Directives
62-Call-By-Reference Function Parameters
63- & Call-By-Reference operator
64-const Applied to Function Parameter Declaration
65-Code: Procedural Voltage Drop Calculations with Reference
Arguments
66-Reference Return Values
67-Code: Procedural Voltage Drop Calculations with Reference
Return Values
68-Creating Classes and Objects
69-Code: Object-Oriented Voltage Drop Calculations for a
Resistor
70-Conditional Compilation Preprocessor Directives
71-public, private Keywords
72-Data Abstraction
73-Constructor Functions
74- : Initialization List Separator
75- . Class Member Access Operator or Dot Operator
76-const Applied to Function Header
77-inline Keyword and Functions
78-Defining Member Functions Outside of Class Declaration
79-Code: Object-Oriented Voltage Drop Calculations for a
Resistor with Member Functions Defined Outside of the Class
Declaration
80-Using User Defined Types
81-Code: Using User Defined Types
25
25
26
27
28
28
29
29
30
31
32
33
33
33
34
35
36
37
38
39
39
39
39
40
40
40
41
41
42
44
44
Writing Programs as a Set of Interacting
Objects
1-Object Relationships
2-Client-Server Interactions
3-Inheritance
4-Public Inheritance
5-Code: Object-Oriented Voltage Drop Calculations with
Inheritance - Components and Resistors
6-Inheritance and Constructors
7-Shadowed Functions
8-One-Dimensional Arrays
9-Multi-dimensional Arrays
10-Array Initialization
11-Arrays and Constructors
12-Arrays as Function Arguments
45
46
46
46
47
49
49
50
51
52
53
54
3
13-Code: Object-Oriented Circuit Solution
14-Memory Management and Pointers
15- * Dereferencing or Indirection Operator
16- -> Arrow Operator and Pointer Initialization
17-void Pointer
18-One-Dimensional Arrays and Pointers
19-new Operator for Requesting Memory from Heap
20-delete Operator for Returning Memory to Heap
21-Three Methods for Dereferencing Pointers of Arrays of
Objects
22-Memory Leaks
23-Code: Dynamic Memory Allocation for a 2-Dimensional Array
24-Virtual Functions
25-Code: Base Class Pointers, Inheritance, and No Virtual
Functions
26-Runtime Polymorphism
27-Code: Base Class Pointers and Inheritance with Virtual
Functions
28-Pure Virtual Functions
29-Code: Pure Virtual Functions
30-protected Class Member Access
31-Code: Circuit Solution (for Node Voltages) as a Set of
Interacting Objects
56
59
60
61
62
62
63
63
65
66
67
67
68
69
70
71
72
73
74
Writing Programs In Mathematical
Languages
1-this Keyword and Special Pointer
2- ?: Conditional Operator
3- , Comma Operator
4-Incrementing and Decrementing Shorthand Operators
5-Shorthand Assignment Operators
6-switch Statement
7-Enumeration: Programmer Defined Integral Type
8-Code: Using Enumeration in Component Class
9-Class friends
10-Overloading the Output Operator as a Friend
11-Code: Overloading Output Operator for Resistor Class
12-Overloading Operators
13-Operator Overloading Restrictions
14-Overloading the Assignment Operator
15-Code: Another assignment operator overload for the
Resistor class
16-Overloading the Addition Operator
17-Code: Adding addition operator overload to the Resistor
class and Overloading the || operator for resistors in
parallel
18-Overloading Unary Operators
19-Code: Adding unary operator overload to Resistor class
20-Conversion Functions
21-Code: Adding conversion function to the Resistor class
22-Static Member Variables and Functions
23-Destructors: Last Will and Testament
24-Code: Adding destructor to the Component class
79
80
81
82
83
84
85
86
87
87
88
91
91
92
92
93
93
94
95
95
96
97
97
98
4
25-Copy Constructors
26-Code: Adding copy constructor to Resistor class
27- try, catch, throw: Exception Handling
28-Nested Class Declarations and Exception Handling
29-Code: Adding exception handling to the Resistor class
30-Call-by-Reference with Pointers
31-Recursive Functions
32-Code: Circuit model with exception handling added to the
Component class
99
100
101
103
104
106
107
108
Software Development Practices and
Principles
1
2
3
4
5
6
Experiment in Small Programs
Step Through Code Line-by-Line
Trail-and-Error Development
No-Duplication Principle
Keep-It-Simple Principle
Objects are Characterized by Behavior
116
5
Object-Oriented Programming
1-Basic elements of programs
1 - Instructions
2 - Data
2-Object-Oriented Versus Procedural
Programming
Procedural program - Data flows around the program
Object-oriented progam - Messages flow around program
Object-oriented program is written around a group of interacting
objects where each object maintains its own data
C++ also supports generic programming
3-C++ Compilation and Linking
Source
Code

*.cpp
Object
Code
*.obj

Executable
Code
*.exe
During compilation:
1 - Textual substitution during pre-processor stage
2 - Assign memory addresses to variables
3 - Translate arithmetic and other operations into machine instructions
During linking:
1-- Code from other libraries is included
2-- Unknown addresses are resolved
Separately compiled language  combine many object files into one
executable
6
4-Objects
Software package which contains related instructions and data
Characteristics:
1 - Name
2 - Behaviors ( instructions )
3 - Attributes ( data )
4 - Relationships with other objects
5-Stages of Program Development
Object-oriented analysis: identify names
Object-oriented design: identify behaviors, attributes, relationships
Object-oriented programming: translate design into code
6-Code: Some C++ Program Anatomy
// A comment
/* Multi-line comments cannot be
nested */
// Start of program code
#include <iostream.h>
#define SUCCESS 0
int main()
{
int i = 1;
double d = 1.0;
cout << "Please enter an integer "
"followed by a double: ";
cin >> i >> d;
cout << "The integer and double you "
"entered are: "
<< i
<< "
"
<< d
<< endl;
return SUCCESS;
}
/* End of program code */
7
7-Bugs

Compilation errors: syntax errors
Easiest to fix

Link errors: Can not find something

Runtime errors: 2 types
Hardest to fix
1 - Program periodically crashes
2 - Logic errors
8-Tokens
Compiler parses program into a series of tokens





Identifiers
Keywords
Constants
Operators
Separators
The meaning depends upon syntax
9-Identifiers 







names we devise
Letters, digits, underscore
Case sensitive
Separated by operators or separators
In expressions are operands
Should not select identifier which is keyword
Should not select "main", "cin", or "cout"
No embedded "white space"
First 1024 characters significant
Examples:
Main
Stuff_1
_stuff
dropVolts
drop_volts
$XYZ
big-top
1goUP
two tokens
8
10-Keywords 


reserved names
74 in all
Always lower case
Pre-assigned meanings which may depend upon syntax
Examples:
int
double
return
11-Constants -
values that cannot change
Examples:
500
-500 (an integer constant expression)
3.14
'a'
"C++ is fun"
12-Operators
- symbols which represent instructions to the
computer to perform some action where action taken may depend upon
operands










Examples:
<<
>>
()
[ ]
Unary operators
Binary operators
Ternary operator
Function call operator
Comma operator
Approximately 52 operators
Action often depends upon operand(s)
Have precedence and associativity

a = x * z + y / v
15 levels of precedence
When operators have same precedence, associativity is applied
 Left-to-right
 Right-to-left
9
13-Separators
- used to define statements, groups of
statements, lists of identifiers, to control order of evaluation, and
include "white space"
Examples
;
{
,
(
:
}
)
14-White space






Comments
Blanks
Tabs
New lines
Ignored by compiler
Should not occur within a token

<
<
Free form language
x =
1 +
y;
x = 1 + y;
15-Programming Style -
the way code is written and
internally documented




Way white space is used
Choosing identifier names
 Type of identifier
 Engineering units
Types of comments that are included
 Verbose comments hide structure
 Do not comment the obvious
 Comments in C++ textbooks
Self documenting code
10
16-Built-in Data Types
Also referred to as fundamental types
char
-128
to
127
short
-32768
to
32767
int
-2147483648
to
2147483647
long
-2147483648
to
2147483647
float
-3.4E-38
to
3.4E38
to
1.7E308
to
65535
double
1.7E-308
void
no value
wchar_t
0
bool
true, false


true is nonzero
false is zero
Examples:
char ch = 'z';
short s = 2;
int n = 50000;
double d = 1.0;
void *pvoid;
wchar_t wch = 'm';
bool b = true;
ASCII character code defines correspondence between small integers and
characters
'1' ~ 49
'A' ~ 65
'B' ~ 66
'a' ~ 95
Certain characters are represented with multiple symbols
 Start with backslash \
 Escape sequence
'\n' ~ new line
' \" ' ~ double quote
11
17-unsigned keyword


only applies to integral data types
model only positive integers
unsigned int n = 4000000000;
18-Declaration


Example:
Provides information to compiler
Memory is not set aside for program use
extern int n;
19-Definition


Example:
Implies a declaration has occurred
Memory is allocated for storing program elements
int n;
20-Initialization


Example:
Implies a definition and a declaration have occurred
Causes memory to be set to a specified value
int n = 1;
21-Addresses
Every variable (identifier) has two values
 Value of variable
 Address
&
address of operator
double d;
d = 1.0;
// Output value of d
cout << d << endl;
// Output hexadecimal address of d
cout << &d << endl;
// Output decimal address of d
cout << (unsigned)&d << endl;
12
Sample output:
1
0x0065FDF0
6684144
22- = assignment operator
x = 1;
x = x + 1;
Expressions return values
// Associativity, right-to-left
y = x = x + 1;
y = ( x = x + 1 );
// Not assignment, but initialization
int n = 1,
m = m;
23- sizeof
operator for determining number of bytes of storage
Attributes of variables:
 Name
 Type (Size)
 Address
 Value
int n = 1;
cout << sizeof( n );
cout << sizeof( int );
Consider another approach
int n;
n = sizeof( int );
Works, but technically incorrect
sizeof() returns programmer defined type size_t
Why do this?

size_t
Makes code portable
n;
n = sizeof( int );
13
cout << n;
Type size_t declared in stddef.h and stdlib.h


Generally unsigned int
typedef unsigned int size_t;
24-Debugger
Associates each line of source code with corresponding machine
instructions in the executable
Pause execution on line of code to investigate variable values
In debugger


Step one line at a time
Run to break point
Breakpoint types



Location
Conditional
Conditional location
25-Arithmetic Expressions
+
*
/
%
addition
subtraction
multiplication
division
modulus or remainder
n = 7 / 3;
cout << n;
n = 7 % 3;
cout << n;
Operator
*
/
%
+
output <<
cout
Precedence
Level
12
12
12
11
11
10
<< 6 + 3 << '\n'
<< 6 - 3 << '\n'
14
<< 6 * 3 << '\n'
<< 6 / 3 << endl;
cout << ( x = y * z ) << endl;
26-Precedence and Associativity
Precedence - order of evaluation of operators
in an expression
Associativity - comes into play for operators
that have same precedence
Use parenthesis to control order of evaluation
n =
i * j * k + l *
m - n ;
n = ( i * j *( k + l * ( m - n ) ) );
Expressions in inner most parenthesis always evaluated first
27-Expressions with Multiple Data Types
int n = 1;
double d = 5.0,
sum = 0.0;
sum = n + d;


Automatic type conversion
Unnamed temporary variables
Automatic type conversion rule

Type that is best able to represent both operands is selected
char and bool variables may be used in arithmetic expressions
bool b = true;
char ch = 'A';
int n = b + ch;
cout << n;
15
28-Ranking for Conversions
unsigned short, short, unsigned char, and
char always => int
int





unsigned int
long
unsigned long
float
double
29-Narrowing Conversions
Occurs with assignments when information is loss
double d = 1.5;
int n = 0;
n = d;
30-Explicit Conversions or Cast Operators
(type) variable
type( variable )
static_cast<type>(variable)
d = (double) n;
d = double(n);
d = static_cast<double>(n);
n = static_cast<int>(d);
To print int value
cout << (int) 'A' << '\n';
cout << (int) 'B' << "\n";
To print char value
cout << (char)65 << endl;
16
31-Functions
()
function call operator
int function_name()
{
// function block
}



// header
Independent block of code that may be executed repeatedly
Basic building block of program organization
All executable statements must occur in function block
32-Code: Procedural Voltage Drop
Calculation
// prog.h
#include <iostream.h>
double voltDrop( double amps, double ohms );
double voltDropTemp( double amps,
double referenceOhms,
double ohmsPerDegree,
double changeInTemp );
// Main.cpp
#include "prog.h"
void main() {
double i1 = 10.0, r1 = 2.0;
cout << "Voltage drop across resistor "
"with no temperature dependency = "
<< voltDrop( i1, r1 )
<< endl;
double i2 = 20.0, r2 = 2.0,
k = 0.1, deltaTemp = 10.0;
cout << "Voltage drop across resistor "
"with temperature dependency = "
<< voltDropTemp( i2, r2, k, deltaTemp )
<< endl;
}
17
// file1.cpp
#include "prog.h"
double voltDrop( double amps, double ohms )
{ return amps * ohms; }
double voltDropTemp( double amps,
double referenceOhms,
double ohmsPerDegree,
double changeInTemp ) {
return amps * ( referenceOhms +
ohmsPerDegree * changeInTemp );
}
Four things may be done with functions




Prototyped (declaration)
 Many times
 Must appear before call
 Specify parameter types
Defined
 Only once
 Specify parameter types and names
Call
 Many times
 May be used anywhere return type may be
 Specify arguments
Take address
Prototypes help compiler in 3 ways:
1)
2)
3)
Generate correct code for return type
Pinpoint illegal type conversions between arguments and parameters
Detect differences between number of arguments and parameters
Three ways in which values are passed:

· Call-by-value
 Default method
 Value of function call argument is copied to function
definition parameter
 Function execution cannot modify variables in calling
environment

· Call-by-reference

· Call-by-reference via pointers
18
33- void Data Type
Nothing is actually a type
void printVolts( double volts );
Helps to insure that printVolts() does not get misused
double v1;
v1 = voltDrop( 10.0, 2.0 ) + printVolts( 5.0 );
Consider the following definition of printVolts()
void printVolts( double volts ) {
cout << "Voltage drop across resistor = "
<< volts
<< endl;
}
34-Code: Using a Function Return Value
as an Argument in a Function Call
void main() {
double i1 = 10.0, r1 = 2.0;
printVolts( voltDrop( i1, r1 ) );
double i2 = 20.0, r2 = 2.0,
k = 0.1, deltaTemp = 10.0;
printVolts(
voltDropTemp( i2, r2, k, deltaTemp ) );
}
35-Overloaded Functions
Functions may share the same name as long as there is a difference in
the parameter list





Must differ in either type or number of parameters or both
Compiler may not distinquish between overloaded functions
based on return type
Name mangling
Static polymorphism
Function to run is determined at compile time
19
36-Code: Procedural Voltage Drop
Calculations with Overloaded Functions
// prog.h
#include <iostream.h>
double voltDrop( double amps, double ohms );
double voltDrop( double
double
double
double
amps,
referenceOhms,
ohmsPerDegree,
changeInTemp );
void printVolts( double volts );
// Main.cpp
#include "prog.h"
void main() {
double i1 = 10.0, r1 = 2.0;
printVolts( voltDrop( i1, r1 ) );
double i2 = 20.0, r2 = 2.0,
k = 0.1, deltaTemp = 10.0;
printVolts( voltDrop( i2, r2, k, deltaTemp ) );
}
// file1.cpp
#include "prog.h"
double voltDrop( double amps, double ohms )
{ return amps * ohms; }
double voltDrop( double
double
double
double
amps,
referenceOhms,
ohmsPerDegree,
changeInTemp ) {
return amps * ( referenceOhms +
ohmsPerDegree * changeInTemp );
}
void printVolts( double volts ) {
cout << "Voltage drop across resistor = "
<< volts
<< endl;
}


voltDrop() represents general action
specific action depends upon argument types
20
37-Default Argument Values


Formal parameters may be given default values in function definition
Default values must be provided from right-to-left in the parameter
list
38-Code: Procedural Voltage Drop
Calculations with Default Arguments
// prog.h
#include <iostream.h>
double voltDrop( double amps,
double ohms,
double ohmsPerDegree = 0.0,
double changeInTemp = 0.0 );
void printVolts( double volts );
// Main.cpp
#include "prog.h"
void main() {
double i1 =
printVolts(
double i2 =
k =
printVolts(
}
10.0, r1 = 2.0;
voltDrop( i1, r1 ) );
20.0, r2 = 2.0,
0.1, deltaTemp = 10.0;
voltDrop( i2, r2, k, deltaTemp ) );
// file1.cpp
#include "prog.h"
double voltDrop( double amps,
double ohms,
double ohmsPerDegree,
double changeInTemp ) {
return amps * ( ohms +
ohmsPerDegree * changeInTemp );
}
void printVolts( double volts ) {
cout << "Voltage drop across resistor = "
<< volts
<< endl;
}
21
39-Scope (Visibility) of an Identifier


Scope of an identifier determines what code has access to the
identifier
Where, and sometimes how, a variable is declared determines it scope
1)
Nested block
void f() {
{ int n; }
2) Function block
3) Function formal parameters
4) Outside of function block
 Global namespace
 Program scope
 File scope
}
40-Memory Storage Classes



Stack
Static storage
Heap or free store
41-Linkage of an Identifier


External
 An identifier which may be referred to in a file
different from the one in which it is defined
 Default for functions at file scope
 Default for global variables
Internal
 An identifier which may be referred to only in the file
in which it is defined
42-Lifetime of a Variable or Object
Lifetime (or extent) is time during program execution when memory is
allocated to the variable


Global variables - program lifetime
Local (automatic) variables - go in and out of existence
22
43- :: ~ Global scope resolution operator –
get the one in the global namespace
44- static keyword
- combines scope of local variable with
lifetime of global variable; changes memory storage class from stack to
static storage
int f1( void )
{
static int remember = 0;
int n = 1;
remember = remember + n;
return remember;
}
static keyword - applied to global identifier changes linkage from
external to internal and visibility from program scope to file scope
static int f1( int n )
{ return n * n; }
static int global;
45- extern keyword

creates pure declaration when applied to global variable
extern int n;
46-Code: Illustrating Scope and Lifetime
// prog.h
#include <iostream.h>
double voltDrop( double
double
double
double
amps,
ohms,
ohmsPerDegree = 0.0,
changeInTemp = 0.0 );
extern double v;
// Main.cpp
#include "prog.h"
double v;
static double v1;
void main() {
double v;
double i1 = 10.0, r1 = 2.0;
v = voltDrop( i1, r1 );
{
double v;
23
double i2 = 20.0, r2 = 2.0,
k = 0.1, deltaTemp = 10.0;
v = voltDrop( i2, r2, k, deltaTemp );
::v = v;
}
double v1;
v1 = ::v + v;
cout << "Value of v1 = " << v1 << endl;
}
// file1.cpp
#include "prog.h"
static void printVolts( double volts ) {
static count = 0;
count =
cout <<
<<
<<
cout <<
<<
<<
cout <<
<<
<<
<<
}
count + 1;
"Number of function calls = "
count
'\n';
"Global voltage value = "
v
'\n';
"Voltage drop across resistor = "
volts
'\n'
endl;
double voltDrop( double amps,
double ohms,
double ohmsPerDegree,
double changeInTemp ) {
double volts;
volts = amps * ( ohms + ohmsPerDegree * changeInTemp );
printVolts( volts );
return volts;
}
47- const keyword


applied in variable initializations
internal linkage is default
const int success = 0;
24
48-Code: Illustrating const Variables and
Internal Linkage
// prog.h
#include <iostream.h>
const int success = 0;
double voltDrop( double
double
double
double
void printVolts( double
amps,
ohms,
ohmsPerDegree = 0.0,
changeInTemp = 0.0 );
volts );
// Main.cpp
#include "prog.h"
int main() {
double i1 = 10.0, r1 = 2.0;
printVolts( voltDrop( i1, r1 ) );
double i2 = 20.0, r2 = 2.0,
k = 0.1, deltaTemp = 10.0;
printVolts( voltDrop( i2, r2, k, deltaTemp ) );
return success;
}
// file1.cpp
#include "prog.h"
double voltDrop( double amps,
double ohms,
double ohmsPerDegree,
double changeInTemp ) {
return amps * ( ohms +
ohmsPerDegree * changeInTemp );
}
void printVolts( double volts ) {
cout << "Voltage drop across resistor = "
<< volts
<< endl;
}
25
49-Algorithms
 Finite sequence of precise statements used to solve a problem
 All algorithms known to man can be written using a combination of
sequence, branching, and iteration statements


Branching statements
 Selects next code block for execution
 Boolean expression is integral part of some branching
statements
Iteration statements
 Used to control repeated execution of a block
 Require the following parts:
 Initialization
 Boolean expression
 Update expression
50-Boolean Variables
bool b1 = true;


may take on one of two values, either true or false
true prints as 1 and false prints as 0
51-Boolean Expressions
 An expression that returns either true or false
 May incorporate other types such as int or double
 Non-zero ~ true
 Zero ( 0 or 0.0 ) ~ false
52-Boolean Expression Operators
 Relational operators







Binary operators which return true or false based upon numerical
comparison between operands
>
>=
<
<=
==
!=
greater than
greater than or equal
less than
less than or equal
equal
not equal
 Logical operators


Operate on true and false values
&&
logical and
26


||
!
logical or
unary logical not
Examples:
bool b;
int n = 2;
b = 2.3;
// true
b = (1 == n );
// false
b = ( 3 == 3.0 );
// ???
b = ( ( 3.0 > 2.99 ) &&
( 3.0 < 3.01 ) );
b = ( n = 3 );
// true
// true
b = ( 1 < 4 < 3 );
// true
b = (1.0E99 < 1.0E99 +1.0E-8 );
b = ( 2 > 3 || 4 < 5 );
// true
b = ( 2 > 3 && 4 < 5 );
// false
// ???
With ||, right-hand operand is not evaluated if left-hand operand is
true
d < 1.0E-9 ||
y = n / d;
With &&, right-hand operand is not evaluated if left-hand operand is
false
d > 1.0E-9
&& y = n / d;
53-Pseudo-code


C++ code mixed with English explanations
English explanation not placed in comments
27
54- if-else Branching Statement
 if statement with single C++ statement
if( Boolean Expression )
statement 1;
statement 2;
 Empty or null statement
~ ;
if( Boolean Expression );
statement 1;
statement 2;
 if statement with code block
if( Boolean Expression ) {
Code block
}

Empty or null block ~ {
}
if( Boolean Expression )
{
}
 if-else statement
if( Boolean Expression )
{
Block 1 }
else
{ Block 2
}
if( x < 5 )
{ y = 1; }
else
{ y = 2; }
 if-else-if statements
if( Boolean Expression 1 )
{ Block 1 }
else if( Boolean Expression 2 )
{ Block 2 }
else if( Boolean Expression 3 )
{ Block 3 }
else if( Boolean Expression 4 )
{ Block 4 }
else
{ Block 5 }
28
 Dangling else
if( Boolean Expression 1 )
if( Boolean Expression 2 )
Statement 1;
else
Statement 2;

else is attached to closest if that does not already have an
else
Re-writing above code
if( Boolean Expression 1 )
{
if( Boolean Expression 2 )
Statement 1;
}
else
{
Statement 2;
}
55- exit() Standard Library Function
Nice example of function which returns void
#include <stdlib.h>
void exit( int exit_code );


Function that does not return
Used to terminate program
Code Snippet:
double amps( double volts, double ohms ) {
if( ohms <= 0.0 )
{
cerr << "0 division in amps()";
exit( 1 );
}
return volts / ohms;
}
56- assert() Standard Library Function
#include <assert.h>
void assert( int expression );


Function that does not return
Program terminates if expression evaluates as false
29
Code Snippet:
double amps( double volts, double ohms ) {
assert( ohms > 0.0 );
return volts / ohms;
}
57- for Iteration Statement
for( Initialization Code ; Boolean Expression ; Update Expression )
{
Block
}





Initialization Code is executed only once upon entrance of loop
Boolean Expression is evaluated after Initialization Code and before
execution of Block
After Block executes, Update Expression is evaluated
After Update Expression is evaluated, Boolean Expression is
evaluated
As long as Boolean Expression evaluates to true, loop continues to
execute code Block
Code Snippet:
for( int i = 1, sum = 0; i < 11; i = i + 1 )
{
sum = sum + i;
}
Nested Loops
for( int i = 0; i < 10; i = i + 1 )
{
for( int j = 0; j < 10; j = j + 1 )
{
cout << i << " " << j << endl;
}
}
58- while Iteration Statement
Initialization Code;
while( Boolean Expression )
{
Block containing update expression
}
Code snippet for summing integers 1 – 10
int i = 1, sum = 0;
while( i < 11 )
{
sum = sum + i;
i = i + 1;
}
30
59-Forever Loops and break Statement
Code snippet:
const int forever = 1;
. . .
while( forever )
{
i = i + 1;
if( i > j ) break;
cout << i;
}
for( ; ; )
{
i = i + 1;
if( i > j ) break;
cout << i;
}

break statement can be used in any iteration loop, but must be used
in forever loop
Code snippet:
for( int i = 0; i < 10; i = i + 1 )
{
for( int j = 0; j < 10; j = j + 1 )
{
if( j > i ) break;
cout << i << " " << j << endl;
}
}
31
60- continue Statement

May only appear in iteration loops

while loop
 Causes jump to evaluation of Boolean expression

for loop
 Causes jump to the evaluation of the update expression
Code snippets:
for( int i
{
i = i +
if( i <
cout <<
}
= 1, j = 3;
i < 5;
1;
j ) continue ;
i * j << endl;
int i = 1, j = 3;
while( i < 5 )
{
i = i + 1;
if( i < j ) continue ;
cout << i * j << endl;
}
cout << i )
32
61-Conditional Compilation with
Preprocessor Directives
Preprocessor directives and operators
#if
if non-zero, include code
#elif
else if non-zero, include code
#else
else if non-zero, include code
#endif
end of logical if
==
tests for equality of operands
# must be the first non-whitespace character on the line
In header file:
#define DEBUG 0
In source file:
#if DEBUG
cout << "Value of x = " << x << endl;
#endif
// Some more code
#if DEBUG
cout << "Value of y = " << y << endl;
#endif
Consider following example:
In header file:
#define NT_CODE
1
#define W95_CODE 2
#define DEBUG NT_CODE
In source file:
#if DEBUG == NT_ CODE
// NT code goes here
#elif DEBUG == W95_CODE
// Windows 95 code goes here
#else
// Type of code not known
#endif
33
62-Call-By-Reference Function Parameters

Call-by-value
 Function parameters are placed on stack when function is called
 Value of function call argument is copied to function parameter
 Changes made to function parameters have no effect on function
call arguments when the function returns

Call-by-reference
 Two methods of implementation
 Call-by-value with pointers
 Reference parameters
 Changes made to a function parameter change the value of the
argument back in the calling environment
 Argument cannot be a literal constant
 More efficient than call-by-value because only address is passed
63- & ~ Call-by-reference operator



Address of an argument is implicitly passed
Function parameter is an alias for the argument in the calling
environment
Within the function, reference parameters are automatically
dereferenced
64- const
applied to function parameter declaration provides
protection of pass-by-value with efficiency of pass-by-reference
34
65-Code: Procedural Voltage Drop
Calculations with Reference Arguments
// prog.h
#include <iostream.h>
#include <assert.h>
const int success = 0;
double voltDrop( double
&volts,
const double &amps,
const double &ohms,
const double &ohmsPerDegree = 0.0,
const double &changeInTemp = 0.0 );
double amps( double &amps,
const double &volts,
const double &ohms );
void printVolts( const double &volts );
void printAmps( const double &amps);
// file1.cpp
#include "prog.h"
double voltDrop( double &volts,
const double &amps,
const double &ohms,
const double &ohmsPerDegree,
const double &changeInTemp ) {
return volts = amps * ( ohms +
ohmsPerDegree * changeInTemp );
}
double amps( double &amps,
const double &volts,
const double &ohms ) {
assert( ohms > 0.0 );
return amps = volts / ohms;
}
void printVolts( const double &volts ) {
cout << "Voltage drop across resistor = "
<< volts
<< endl;
}
void printAmps( const double &amps) {
cout << "Current flow through resistor = "
<< amps
<< endl;
}
35
// Main.cpp
#include "prog.h"
int main() {
double i1 = 10.0, r1 = 2.0, v1;
printVolts( voltDrop( v1, i1, r1 ) );
double i2 = 20.0, r2 = 2.0, v2,
k = 0.1, deltaTemp = 10.0;
printVolts( voltDrop( v2, i2, r2,
k, deltaTemp ) );
cout << v1 + v2 << endl;
double v3 = 100.0, r3 = 20.0, i3;
printAmps( amps( i3, v3, r3 ) );
return success;
}
66-Reference Return Values
A function may return a reference value


As long as it is not a const reference value, such a function
may be used on the left hand side of an assignment operator
A persistent object must be referenced
36
67-Code: Procedural Voltage Drop
Calculations with Reference Return Values
// prog.h
#include <iostream.h>
#include <assert.h>
const int success = 0;
double& voltDrop( double &volts,
const double &amps,
const double &ohms,
const double &ohmsPerDegree = 0.0,
const double &changeInTemp = 0.0 );
double& amps( double &amps,
const double &volts,
const double &ohms );
void printVolts( const double &volts );
void printAmps( const double &amps);
// file1.cpp
#include "prog.h"
double& voltDrop( double &volts,
const double &amps,
const double &ohms,
const double &ohmsPerDegree,
const double &changeInTemp ) {
return volts = amps * ( ohms +
ohmsPerDegree * changeInTemp );
}
double& amps( double &amps,
const double &volts,
const double &ohms ) {
assert( ohms > 0.0 );
return amps = volts / ohms;
}
void printVolts( const double &volts ) {
cout << "Voltage drop across resistor = "
<< volts
<< endl;
}
void printAmps( const double &amps) {
cout << "Current flow through resistor = "
<< amps
<< endl;
}
37
// Main.cpp
#include "prog.h"
int main() {
double i1 = 10.0, r1 = 1.0, v1;
double v2, i2, r2 = 2.0;
printVolts( v2 = voltDrop( v1, i1, r1 ) );
printAmps( amps( i2, v2, r2) );
return success;
}
68-Creating Classes and Objects
class
keyword for defining user defined type
public, private
defined type
:
.
keywords for defining access to members of user
initialization list separator
class member access operator or dot operator




member variables and member functions belong to the scope of
the class
only way to access class members is by an invoking object
each object has its own copy of member variables
objects share member functions
38
69-Code: Object-Oriented Voltage Drop
Calculations for a Resistor
// resistor.h
#ifndef __Resistor_h
#define __Resistor_h
class Resistor { public:
Resistor(double _amps = 0.0,
double _refOhms = 1.0,
double _dT = 0.0,
double _k = 0.1 ) :
amps( _amps ),
refOhms( _refOhms ),
dT( _dT ), k( _k )
{ calcOhms(); v(); }
void setdT( double _dT )
{ dT = _dT; calcOhms(); }
double& v() { return volts = amps * Ohms; }
double& i() { return amps = volts / Ohms; }
double ohms() const { return Ohms; }
void print() const {
cout << "Voltage drop across resistor =
"
<< v()
<< '\n'
<< "Current through resistor =
"
<< i()
<< endl;
}
private:
void calcOhms() {
Ohms = refOhms + k * dT;
assert( Ohms > 0.0 );
}
double volts, amps, refOhms, Ohms, dT, k;
};
#endif
// Main.cpp
#include <iostream.h>
#include <assert.h>
#include "resistor.h"
void main() {
Resistor r1( 10.0, 1.0 ),
r2(0.0, 2.0 );
r2.v() = r1.v();
r2.print();
}
39
70-Conditional Compilation Preprocessor
Directives
#ifndef __Resistor_h
#define __Resistor_h
// header file statements go here
#endif
71- public, private keywords
Class Member Access Specifiers





Specify access to class members from outside of class
Default access is private
Encapsulation
Public interface or behavior
Implementation
72-Data Abstraction






Functional interface to class member variables
 Readers
 Writers
 Reader and writer
Protect users of class from modifications
From experience, attributes change due to new requirements
Encapsulation
Extremely important to identify the public interface, or
behaviors, of the class needed by the users
Objects Are Characterized By Behaviors
73-Constructor Functions







Everytime an object is defined a constructor function
executes
 Compiler provides default constructor
Special type of class member function which provides for
initialization of objects
 Default constructor function does not take any
parameters
Same name as class
Cannot return a value, not even void
Automatically runs when objects are defined
Usually overloaded
Otherwise, like normal functions
40
74- : initialization list separator

Assignment operator does not work in initialization list
75- . class member access operator or dot
operator

Used by invoking object to access public members
Consider
class Bridge{
public:
Resistor r1, r2, r3, r4, r5;
};
Bridge b1, b2;
b1.r1.dOhms = b1.r2.dOhms
= b1.r3.dOhms
= b1.r4.dOhms
= b1.r5.dOhms
= 1.0;
b2 = b1;


default definition of assignment operator (binary copy)
default constructor also provided by compiler if no
constructor defined
Resistor r1, r2, r3;
r1 = r2 + r3;

would not make sense to compiler
76- const Applied to Function Header

Specifies member function does not change invoking object
member variables
41
77- inline Keyword and Functions

Compiler replaces function call with body of function
 Formal parameters are replaced with actual arguments
inline int f( int n )
{ return
3.1427 * n;
}
int main() {
int m1 = 2,
m2 = 4;
cout << f( m1 );
cout << 3.1427 * m1;
// we type
// inline modification
cout << f( m2 );
cout << 3.1427 * m2;
// we type
// inline modification
return 0;
}




Request to compiler
Execution time reduced at expense of program size
Inline functions have internal linkage
 Place in header files
Compiler attempts to inline class member functions that are defined
in class declaration
78-Defining Member Functions Outside of
Class Declaration
class_name:: ~ class scope resolution operator
42
79-Code: Object-Oriented Voltage Drop
Calculations for a Resistor with Member
Functions Defined Outside of the Class
Declaration
// resistor.h
#ifndef __Resistor_h
#define __Resistor_h
#include <assert.h>
class Resistor { public:
Resistor(double _amps = 0.0,
double _refOhms = 1.0,
double _dT = 0.0,
double _k = 0.1 );
void setdT( double _dT );
void setOhms( double _refOhms );
double& v();
double& i();
double ohms() const;
void print() const;
private:
void calcOhms();
double volts, amps, refOhms, Ohms, dT, k;
};
inline double& Resistor::v()
{ return volts = amps * Ohms; }
inline double& Resistor::i()
{ return amps = volts / Ohms; }
inline double Resistor::ohms() const
{ return Ohms; }
inline void Resistor::calcOhms() {
Ohms = refOhms + k * dT;
assert( Ohms > 0.0 );
}
#endif
43
// resistor.cpp
#include <iostream.h>
#include "resistor.h"
Resistor::Resistor(
double _amps,
double _refOhms,
double _dT,
double _k ) :
amps( _amps ),
refOhms( _refOhms ),
dT( _dT ), k( _k )
{ calcOhms(); v(); }
void Resistor::setdT( double _dT )
{ dT = _dT; calcOhms(); }
void Resistor::setOhms( double _refOhms )
{ refOhms = _refOhms; calcOhms(); }
void Resistor::print() const {
cout << "Voltage drop across resistor =
<< v()
<< '\n'
<< "Current through resistor =
"
<< i()
<< endl;
}
// Main.cpp
#include "resistor.h"
void main() {
Resistor r1( 10.0, 1.0 ),
r2(0.0, 2.0 );
r2.v() = r1.v();
r2.print();
}
"
44
80-Using User Defined Types



Parameter types in function declarations and definitions
Function return types
Objects can be passes as arguments
81-Code: Using User Defined Types
Resistor sumResistances( Resistor r1, Resistor r2 ) {
Resistor r3;
r3.setOhms( r1.ohms() + r2.ohms() );
return r3;
}
void main() {
Resistor rx( 0.0, 10.0 ),
ry( 0.0, 20.0 ),
rz;
rz = sumResistances( rx, ry );
cout << rz.ohms() << endl;
}
45
Writing Programs as a Set of Interacting
Objects
1-Object Relationships



Name classes and objects
Specify class attributes
Specify behaviors

Four types of relationships that an object can have with other
objects
 Inheritance
 Containment
 "Relates to"
 "Uses a"

Containment
 "Has a" relationship
 Aggregation or composition
class A {

public: B b;
C c;
};
"Relates to"
 Selected attributes of one object are compared for equality with
attributes of another object
class A{ public: Key key;
…… };
class B{ public: Key key;
…..
};
A a;
B b;
if( a.key == b.key ) ….

"Uses a"
 One object makes use of the services another object
int A::f( int n )
{
B b(n);
return b.someService();
}

With "uses a", object providing services only exists for as long as
services are needed
46
2-Client-Server Interactions
In all interactions, one object, the client, makes use of the services
offered by another object, the server
3-Inheritance

Used to model object relationships when different entities have
behaviors or attributes or both in common

Base class: contains common behaviors and/or attributes
 General category (fruit)

Derived class: inherits common behaviors and/or attributes from base
class
 Specific category (apple)
 Adds specific attributes and/or behaviors
 May modify or override base class behavior

Three different ways that a derived class may inherit from a base
class
 public
 derived class "is a kind of" base class
 apple "is a" fruit
 protected
 private
 Specifies access in the derived class of behaviors/attributes
inherited from the base class
4-Public Inheritance


Public members of base class are also public members of derived
class
 Except for constructors and certain operators
Private members of base class belong to derived class object, but
cannot be directly accessed in derived class
47
5-Code: Object-Oriented Voltage Drop
Calculations with Inheritance Components and Resistors
//component.h
#ifndef __component_h
#define __component_h
class Component{ public:
Component( double _amps = 0.0 ) :
amps( _amps ), volts( 0.0 ) {
double& v() { return volts; }
double& i() { return amps; }
private: double volts, amps;
};
}
#endif
// resistor.h
#ifndef __Resistor_h
#define __Resistor_h
#include <assert.h>
#include "component.h"
class Resistor : public Component { public:
Resistor( double _amps = 0.0,
double _refOhms = 1.0,
double _dT = 0.0,
double _k = 0.1 );
void setdT( double _dT );
void setOhms( double _refOhms );
double& v();
double& i();
double ohms() const;
void print();
private:
void calcOhms();
double refOhms, Ohms, dT, k;
};
inline double& Resistor::v() {
return Component::v() = Component::i() * Ohms;
}
inline double& Resistor::i() {
return Component::i() = Component::v() / Ohms;
}
48
inline double Resistor::ohms() const
{ return Ohms; }
inline void Resistor::calcOhms() {
Ohms = refOhms + k * dT;
assert( Ohms > 0.0 );
}
#endif
// resistor.cpp
#include <iostream.h>
#include "resistor.h"
Resistor::Resistor(
double
double
double
double
_amps,
_refOhms,
_dT,
_k ) :
Component( _amps ),
refOhms( _refOhms ),
dT( _dT ), k( _k )
{ calcOhms(); v(); }
void Resistor::setdT( double _dT )
{ dT = _dT; calcOhms(); }
void Resistor::setOhms( double _refOhms )
{ refOhms = _refOhms; calcOhms(); }
void Resistor::print() {
cout << "Voltage drop across resistor =
<< v()
<< '\n'
<< "Current through resistor =
"
<< i()
<< endl;
}
// Main.cpp
#include "resistor.h"
#include <iostream.h>
void main() {
Resistor r1( 10.0, 10.0 ),
r2( 0.0, 5.0 );
r2.v() = r1.v();
r2.print();
}
"
49
6-Inheritance and Constructors

Constructors execute when
 Objects defined
 Temporary objects needed in expression evaluations
 Arguments to functions are objects and are passed with callby-value
 Object values are returned from functions

Consider the following example of a constructor being used to create
a temporary object
Resistor r;
r = Resistor( 10.0, 10.0, 1.0, 10.0 );






Constructors with public access may be called from code where
objects corresponding to the constructor type may be defined
In inheritance, constructors are not inherited and must be called
explicitly in the initialization list
In the construction of derived class objects, the base class
constructors run before the derived class constructors
Base class constructors run in the order of the base class
inheritance list, and not in the order they are placed in the
derived class constructor initialization list
After the base class portions of a derived class object are built,
then the constructors for any objects that are contained in the
derived class run
 Contained object constructors run in the order that the contained
objects are defined in the containing class declaration
After the contained portion of a class is built, the constructor for
the class itself runs
7-Shadowed Functions


If a base class and derived class both have a function with the same
name, then the derived class function hides or "shadows" the base
class function
A shadowed function may be used when it is appropriate to modify the
behavior of the base class
50
8-One-Dimensional Arrays

[ ]

~ array operator
Binary operator
char ch[12];
int n[5];
double d[10];
Resistor r[7];
n[0], n[1], n[2], n[3], n[4];





Type and name
Elements or indexed variables
 Must be of the same type
Dimension or size ~ number of elements
Elements are accessed by index or subscript
 For fundamental types must evaluate to integral type
 Index starts from 0
 Max legal index is one less than dimension
The elements of an array of type int may be used anywhere a type int
may be
n[0] = 1;
n[1] = 2;
n[2] = n[0] + n[1];

sizeof operator may be used to determine array size
int n[5];
cout << sizeof( n );

Elements are stored contiguously in memory

Compiler will allow indexing outside of array limits
 Runtime errors
 Efficiency
int n[5];
n[4] = 4;
n[5] = 5;
n[1000] = n[5] + n[4];

Good programming style to define a constant to be used as the size
of the array
51
Consider example of user defined type array
const int dim = 10;
Resistor r[dim];
for( int i = 0; i < dim; ++i )
{ cout << r[i].v() << endl;
}

Assignment of fundamental type arrays must be done element-byelement
int m[3], n[3];
// m = n;
for( int i = 0; i < 3; ++i)
{ m[i] = n[i]; }
9-Multi-dimensional Arrays

[ ][ ][ ]…
~
repeat [ ] operator
char ch[12][80];
int n[5][10];
double d[10][12];
Resistor r[7][7];
double x[10][100][1000];


Laid out contiguously in memory
Max legal index for each individual index is one less than dimension


Address space of physical memory is linear
Storage map ~ layout of array elements in memory
 For matrix, elements are laid out in memory by rows
int mn[2][2];
Address:
Address:
Address:
Address:

1000
1004
1008
1012
mn[0][0]
mn[0][1]
mn[1][0]
mn[1][1]
May consider mn[2][2] to be an array-of-arrays


mn[0][2]
mn[1][2]
52

Understanding storage maps aids in


Initialization of arrays
Writing efficient algorithms
How can we determine storage map for
int z[2][2][2];
?
First element is z[0][0][0]. Then, lower order indices vary the
fastest.
for( int i = 0; i < 2; ++i )
for( int j = 0; j < 2; ++j )
for( int k = 0; k < 2; ++k )
cout << z[i][j][k] << endl;

May consider z[2][2][2] to be an array of matrices


z[0][2][2]
z[1][2][2]
10-Array Initialization
int m[5] = { 1, 2, 3, 4, 5 };
or
int m[5] = { 1, 2

};
m[2], m[3], m[4] initialized to 0
or
int m[] = { 1, 2, 3, 4, 5 };

Initialization of multi-dimensional arrays
int mat[2][3] = {
{ 1, 2, 3 },
{ 4, 5, 6 }
};
where in matrix form
m[2][3] =
|
|
|
int mat[][3] = {

1
2
3
4
5
6
{ 1, 2, 3 },
|
|
|
{ 4, 5, 6 }
};
Do not have to specify highest order index
53
11-Arrays and Constructors
Resistor r[10];

Default constructor executes for each member as it is built
Resistor r[2] = {
Resistor(10., 0.0, 0.0, 0.0),
Resistor(10.0, 0.0, 0.0, 0.0) };
or
Resistor r[2] = {
Resistor(),
Resistor(10.0, 0.0, 0.0, 0.0) };
or
Resistor r[2] = {
Resistor(10., 0.0, 0.0, 0.0),
Resistor() };

Note default constructor is explicitly invoked
54
12-Arrays as Function Arguments

Arrays may be used as function arguments
void f1( int m[5] );
or
void f1( int m[] );

Highest order dimension does not have to be specified
Now consider definition for f()
void f1( int m[] )
{
cout << m[0] <<
<< m[1] <<
<< m[2] <<
<< m[3] <<
<< m[4] <<
}
" "
" "
" "
" "
endl;
An example call to f1() is
void main() {
int m[] = { 1, 2, 3, 4, 5 };
f1( m );
}

Important to pass array dimension
void f1( int m[], int size );
void f1( int m[], int size )
{
for( int i = 0; i < size; ++i )
cout << m[i] << " ";
cout << endl;
}
void main() {
int m[] = { 1, 2, 3, 4, 5 };
f1( m, 5 );
int another[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
f1( another, 8 );
}
55

Multi-dimensional array prototypes
void f2( int x[5][6], int rows );
or
void f2( int x[][6], int rows );

Use pointers to pass matrices with variable number of rows and
columns
int y[10][6];
f2( y, 10 );


Note that only name of array is passed
Arrays are passed call-by-reference
void f3( int m[] )
{
m[0] = 1;
}
void main() {
int m[] = { 0, 0, 0 };
f3( m );
cout << m[0] << endl;
}
56
13-Code: Object-Oriented Circuit Solution
//component.h
#ifndef __component_h
#define __component_h
#include <string.h>
class Component{ public:
Component( double _amps = 0.0,
double _volts = 0.0,
char _name[ ] = "No name" ) :
amps( _amps ),
volts( _volts )
{ strcpy( name, _name ); }
double& v() { return volts; }
double& i() { return amps; }
void print();
private: double volts, amps; char name[32];
};
#endif
//component.cpp
#include <iostream.h>
#include "component.h"
void Component::print()
{
cout << name
<< ": "
<< "Volts = "
<< volts
<< " "
<< "Amps = "
<< amps
<< endl;
}
// source.h
#ifndef __source_h
#define __source_h
#include "component.h"
class Source : public Component {
public:
Source( double _volts = 100.0,
char _name[ ] = "100 Volt Source" ):
Component( 0.0, _volts, _name ) { }
};
#endif
// load.h
#ifndef __load_h
#define __load_h
57
#include "component.h"
class Load : public Component {
public:
Load( double _amps = 10.0,
char _name[ ] = "10 Amp Load" ) :
Component( _amps, 0.0, _name ){ }
};
#endif
// resistor.h
#ifndef __Resistor_h
#define __Resistor_h
#include <assert.h>
#include "component.h"
class Resistor : public Component {
public:
Resistor( double _refOhms = 1.0,
char _name[ ] = "1 Ohm Resistor",
double _amps = 0.0,
double _dT = 0.0,
double _k = 0.1 );
void setdT( double _dT );
void setOhms( double _refOhms );
double& v();
double& i();
double ohms() const;
private:
void calcOhms();
double refOhms, Ohms, dT, k;
};
inline double& Resistor::v()
{ return Component::v() = Component::i() * Ohms; }
inline double& Resistor::i()
{ return Component::i() = Component::v() / Ohms; }
inline double Resistor::ohms() const
{ return Ohms; }
inline void Resistor::calcOhms() {
Ohms = refOhms + k * dT;
assert( Ohms > 0.0 );
}
#endif
58
// resistor.cpp
#include <iostream.h>
#include "resistor.h"
Resistor::Resistor(
double _refOhms,
char _name[ ],
double _amps,
double _dT,
double _k ) :
Component( _amps, 0.0, _name ),
refOhms( _refOhms ),
dT( _dT ), k( _k )
{ calcOhms(); v(); }
void Resistor::setdT( double _dT )
{ dT = _dT; calcOhms(); }
void Resistor::setOhms( double _refOhms )
{ refOhms = _refOhms; calcOhms(); }
// Main.cpp
// Solve circuit with source, resistor,
// and load in series
#include "resistor.h"
#include "source.h"
#include "load.h"
#include <iostream.h>
void main() {
Resistor r( 10.0, "10 Ohm Resistor" );
Load load( 10.0, "10 Amp Load" );
Source sour( 200.0, "200 Volt Source" );
r.i() = load.i();
load.v() = sour.v() - r.v();
sour.i() = r.i();
sour.print();
r.print();
load.print();
}
59
14-Memory Management and Pointers
Pointer ~ a variable which stores a memory
address


address of a variable
address of a function


Each byte in computer memory has an address
Most variables require more than one byte of storage
 Address of a variable is the address of the first byte that
is used to store the variables value
All pointers have the same size



( & )
Size of arrays do not need to be defined at compile time
Pointers allow algorithms to be written in terms of addresses
 Write more efficient algorithms
Code Snippet:
void main() {
double d = 1;
cout << unsigned (&d );
d = 2.0;
}


Value of d may be changed
Address where d is stored is a constant
Definition of a pointer to a variable of type double:
double d,
d1;
double *pd;



the variable pd is of type pointer to double
pd does not store a double
pd stores the address where a double is stored
pd = &d;
pd = &d1;

The address that pd stores may be changed
A pointer to int is defined as
int *pn;
A pointer to Resistor is defined as
Resistor *pR;
60
Pointer base type:


Base type of pn is int
Base type of pR is Resistor
sizeof( pn ) is the same as sizeof( pR)
However, pn is different that pR
Resistor r;
pn = &r;


// Compiler error
This is because a pointer may be used to obtain the value of
what it points to
The way the chunk of memory is interpreted depends upon what
is stored there
Casts may be used to get around the above, but are not recommended
double d = 100.0;
int *pn;
pn = (int*)(&d) ;
cout << *( (double*)(pn) ) << endl;
15- * ~ dereferencing or indirection
operator
double d = 2.0,
*pd = &d;
cout << d;
cout << *pd;

*pd is another name for d
char ch = 'A';
cout << *&ch;
is the same as
cout << ch;
61
Resistor
r( 10.0, 0.0, 0.1, 10.0 ),
*pr = &r;
r.v();
is the same as
(*pr).v();

parenthesis are required to force the dereferencing of the
pointer prior to the evaluation of the dot operator
16- -> Arrow Operator and Pointer
Initialization

shorthand notation for
( * ).
(*pr).v();
is the same as
pr->v();
Initialization of pointers
double *pd;
d = 2.0 + *pd;
*pd = 2.0;

Where is the value of 2.0 written?

Junk value stored in pointer may change between runs of
program
 Debugging nightmare
If a pointer can not be initialized to a valid
address, then it should be initialized to the
"invalid address" of 0
int *pn = 0;


Compiler will allow a NULL pointer (ie, set to 0) to be used
However, at runtime, an unhandled exception will be thrown if
dereferenced pointer is used
 Prevents overwriting
62
17- void Pointer
void *pVoid = 0;

void x;


No value associated with type void
// compiler error
A void pointer may be used to store an address
void pointer may not be dereferenced because it does not know
what it points to
void *pVoid = NULL;
int n = 2, *pn = &n;
double d = 3.0, *pd = &d;
pVoid = pn;
cout << "Address of int n = "
<< unsigned( pVoid )
<< endl;
pVoid =
cout <<
<<
<<
pd;
"Address of double d = "
unsigned( pVoid )
endl;
cout << *pVoid;
// compiler error
One use of void pointers is as a function return type
 A type cast must be used to obtain the value
18-One-Dimensional Arrays and Dynamic
Memory Management




Dynamic Memory Management for fundamental types
Dynamic object creation
Heap or free store
Programmer control over variable or object lifetime
63
19- new ~ operator used to request memory
from heap
Operator new returns pointer to start of contiguous memory

NULL pointer is returned if sufficient memory is not available
Code snippet for fundamental type:
double *pd = 0;
int size;
cout << "Input size of array that is needed"
<< endl;
cin >> size;
pd = new double[size] ;
if( !pd )
{
cout << "Memory allocation failed"
<< endl;
exit(1);
}
for( int i = 0; i < size; ++i )
{ pd[i] = double(i); }
20- delete ~ operator used to return memory
to heap
Operator delete is said to free the memory
delete [ ] pd ;
pd = 0;
If the memory being freed is for an array, then must use [ ] in delete
statement
pd = new double[size];
delete [ ] pd;
64
If memory being freed is not an array
pd = new double;
delete pd;
Code snippet for user defined type where single object is allocated:
Resistor *pR = 0;
pR = new Resistor( 10.0, 0.0, 0.1, 10.0 );
if( !pR ) { exit(1); }
pR->print();
delete pR ;
pR = 0;
Code snippet for user defined type where array of objects are
allocated:
Resistor *pR = 0;
pR = new Resistor[5];
assert( pR );
for( int i = 0; i < 5; ++i )
{ pR[i].print() ; }
delete [ ] pR;
pR = 0;

If [ ] are left out, then only memory for first object will be
freed
User defined types



Dynamic object creation
Constructors automatically run when new operator obtains
memory
Destructors automatically run when delete operator returns
memory
Indexing outside of dynamically allocated array limits:
double *pd = new double[10];
pd[10];
// not caught by compiler
65
21-Three methods for dereferencing pointers
of arrays of objects
(*(pR + i)).print();
is the same as
(pR + i)->print();
is the same as
pR[i].print() ;

pR + i ~ a pointer to a Resistor
 pointer arithmetic
 i chunks of memory corresponding to sizeof(Resistor) are
skipped

pR[i] ~
*(pR + i)
Pointer arithmetic is used by the compiler to locate elements in arrays
 Elements in an array must be of the same size
 Only addition and subtraction make sense
 Only arithmetic on pointers that are referenced to the same
array makes sense
Code Snippet:
Resistor *pR = NULL,
*new_pR_Address = NULL;
int i = 3;
pR = new Resistor[5];
assert( pR );
new_pR_Address = pR + i;
i = new_pR_Address - pR ;
66
22-Memory Leaks
void obtainMemory( int n )
{
double *pd = NULL;
pd = new double[n];
}



pd is a local variable
pd no longer exists after function execution
memory is still allocated on heap, but program losses the
address
Code to correct the memory leak:
double * obtainMemory( int n )
{
double *pd = NULL;
pd = new double[n];
assert( pd );
return pd;
}
Memory should be freed prior to program finish
void freeMemory( double *pd )
{
delete [ ] pd;
}
67
23-Code: Dynamic Memory Allocation for a 2Dimensional Array
#include <iostream.h>
#include <assert.h>
double **getMatrix( int rows, int cols ) {
double *arr, **mat;
arr = new double[ rows * cols ];
assert( arr );
mat = new double*[ rows ];
assert( mat );
for( int i = 0; i < rows; ++i )
{
mat[i] = arr + ( i * cols );
for( int j = 0; j < cols; j++ )
mat[i][j] = i + j;
}
return mat;
}
void freeMatrix( double **mat ){
delete [] *mat;
delete [] mat;
}
void main() {
double **mat1 = getMatrix( 3,3 );
cout << mat1[2][2] << endl;
freeMatrix( mat1 );
}
24-Virtual Functions
Previous rule: A pointer may only be used to point to an object of the
pointer's base type
Exception: A pointer declared as a pointer to a base class can also be
used to point to any object derived from the base class if public
inheritance is used

A derived object "is a" base class object

Reverse is not true
68
25-Code: Base Class Pointers, Inheritance,
and No Virtual Functions
class Base{
public:
int n;
void who() { cout << "\nBase"; }
};
class Derived : public Base{
public:
int m;
void who() { cout << "\nDerived"; }
};
void main()
{
Base b, *pb = NULL;
Derived d, *pd = NULL;
b.n = 1;
d.n = 2;
d.m = 3;
pb = &b;
cout << pb->n; /* Outputs 1 */
pb->who(); /* Outputs Base */
pb = &d ;
cout << pb->n; /* Outputs 2 */
cout << pb->m ; /* Compiler error */
pb->who() ; /* Outputs Base */
pd = &d;
pd->who(); /* Outputs Derived */
}

Base class pointer can only access members of the derived object
that were inherited from the base class

When base class pointer is used to invoke a function, the member
function that is executed is determined by the type of pointer
 Member function is not determined by the type of object pointed
to
69
26-Runtime Polymorphism

Virtual function ~ member function of the base class that is
redefined by a derived class
 Polymorphic class
 Virtual function hierarchy
 Share the same name, like overloaded functions (static
polymorphism)
 Unlike overloaded functions, all functions must have identical
prototypes
 Base class declaration sets the design pattern

Virtual function to run is selected at program runtime, and not at
compile time
 Switching logic is handled automatically at runtime
 Function that executes is determined by the type of object being
pointed to
 A virtual function in a base class is said to be overridden when
it is redefined in a derived class
70
27-Code: Base Class Pointers and
Inheritance with Virtual Functions
class Base{
public:
virtual void who()
{ cout << "\nBase"; }
};
class Derived1 : public Base {
public:
virtual void who()
{ cout << "\nDerived1"; }
};
class Derived2 : public Base {
public:
virtual void who()
{ cout << "\nDerived2"; }
};
class Derived3 : public Base {
public:
void noWho() { };
};
void main()
{
Base *pb, b;
Derived1 d1;
Derived2 d2;
Derived3 d3;
pb = &b ;
pb->who() ;
/* Outputs Base */
pb = &d1 ;
pb->who() ;
/* Outputs Derived1 */
pb = &d2 ;
pb->who() ;
/* Outputs Derived2 */
pb = &d3 ;
pb->who() ;
}
/* Outputs Base */

Note that a base class pointer can be used to access virtual
functions in derived classes

Once a function is declared as virtual in a base class, it is
automatically virtual in every class that is derived from the base
class
71
Virtual Table or VTable



Each derived class has a VTable
Rows store virtual function addresses
Every derived object has an extra pointer which points to the
Vtable
Constructor functions cannot be declared as virtual

Constructor functions are not inherited
28-Pure Virtual Functions
A function that is assigned the value of 0
Abstract




class ~ contains one or more virtual functions
Objects corresponding to an abstract class cannot be created
Incomplete data type
Provides design pattern for inheritance
Derived class must override virtual functions
 If not, derived class itself is abstract
72
29-Code: Pure Virtual Functions
class Shapes {
public:
virtual void shape() = 0 ;
};
class Rectangle : public Shapes {
public:
virtual void shape()
{ cout << "Rectangle" << endl; }
};
class Circle : public Shapes {
public:
virtual void shape()
{ cout << "Circle" << endl; }
};
void main()
{
Shapes s ;
/* Compiler error */
Rectangle rect;
Circle circle;
Shapes *ps[ ] = { &rect, &circle, NULL };
int i = 0;
while( ps[i] )
{ ps[i]->shape() ; i++; }
}
A pure virtual function may define a body
class Shapes {
public:
virtual void shape() = 0
{ cout << "Shape" << endl; }
};
Rectangle rect;
rect.Shapes::shape();
73
30-protected Class Member Access



public access specifier
 visible in scope of class and everywhere objects exist
 interface for clients
 specifies behavior of objects
private access specifier
 only visible in scope of class
 default
 to protect class users, place member variables here
protected access specifier
 visible in scope of class and in the scope of derived classes
 services cannot be accessed via containment, must use inheritance
Code snippet:
class Base{
void Private() {
cout << "By default, only visible in "
<< "class Base";
}
public:
void everywhere() {
cout << "Visible everywhere";
}
char ch_Private()
{ return chPrivate; }
void ch_Private( char ch_private )
{ chPrivate = ch_private; }
protected:
void derived() {
cout << "Visible in derived classes";
}
private:
void hidden() {
cout << "Only visible in class Base";
}
char chPrivate;
};
Base b;
b.derived() ;
/* compiler error */
class D1 : public Base {
public:
void useServicesOfBase()
{ derived() ; }
};
74
31-Code: Circuit Solution (for Node
Voltages) as a Set of Interacting Objects
//component.h
#ifndef __component_h
#define __component_h
#include <string.h>
class Component{ public:
Component( double _amps = 0.0,
double _volts = 0.0,
char _name[] = "No name") :
amps( _amps ),
volts( _volts ),
pF(0), pB(0)
{ strcpy( name, _name ); }
virtual const double& v() = 0 { return volts; }
virtual const double& i() = 0 { return amps; }
void setV( double _volts ) { volts = _volts; }
void setI( double _amps ) { amps = _amps; }
void setF( Component *pCmp ) { pF = pCmp; }
void setB( Component *pCmp ) { pB = pCmp; }
Component* f() { return pF; }
Component* b() { return pB; }
void print();
protected: double volts, amps;
char name[32];
Component *pF, *pB;
};
#endif
75
// resistor.h
#ifndef __Resistor_h
#define __Resistor_h
#include <assert.h>
#include "component.h"
class Resistor : public Component { public:
Resistor( double _refOhms = 1.0,
char *_name = "1 Ohm Resistor",
double _amps = 0.0,
double _dT = 0.0,
double _k = 0.1 );
void setdT( double _dT );
void setOhms( double _refOhms );
virtual const double& v();
virtual const double& i();
double ohms() const;
private:
void calcOhms();
double refOhms, Ohms, dT, k;
};
inline const double& Resistor::v()
{ return volts = pB->v() - amps * Ohms; }
inline const double& Resistor::i()
{ return amps = pF->i(); }
inline double Resistor::ohms() const
{ return Ohms; }
inline void Resistor::calcOhms() {
Ohms = refOhms + k * dT;
assert( Ohms > 0.0 );
}
#endif
// source.h
#ifndef __source_h
#define __source_h
#include "component.h"
class Source : public Component {
public:
Source( double _volts = 100.0,
char *_name = "100 Volt Source" ):
Component( 0.0, _volts, _name ) { }
virtual const double& v()
{ return volts; }
virtual const double& i()
{ return amps = pF->i(); }
};
#endif
76
// load.h
#ifndef __load_h
#define __load_h
#include "component.h"
class Load : public Component {
public:
Load( double _amps = 10.0,
char *_name = "10 Amp Load") :
Component( _amps, 0.0, _name ){ }
virtual const double& v()
{ return volts = pB->v(); }
virtual const double& i()
{ return amps; }
};
#endif
//component.cpp
#include <iostream.h>
#include "component.h"
void Component::print()
{
cout << name
<< ": "
<< "Node Volts = "
<< volts
<< " "
<< "Amps = "
<< amps
<< endl;
}
77
// resistor.cpp
#include <iostream.h>
#include "resistor.h"
Resistor::Resistor(
double _refOhms,
char *_name,
double _amps,
double _dT,
double _k ) :
Component( _amps, 0.0, _name ),
refOhms( _refOhms ),
dT( _dT ), k( _k )
{ calcOhms(); }
void Resistor::setdT( double _dT )
{ dT = _dT; calcOhms(); }
void Resistor::setOhms( double _refOhms )
{ refOhms = _refOhms; calcOhms(); }
78
// Main.cpp
#include <iostream.h>
#include "component.h"
#include "resistor.h"
#include "source.h"
#include "load.h"
void main() {
const int numCktEle = 3;
const int numCktEleMinOne = numCktEle - 1;
//Obtain memory and initialize
//circuit components
Component **pCmp = new Component *[numCktEle];
pCmp[0] = new Source( 200.0, "200 Volt Source" );
pCmp[1] = new Resistor( 10.0, "10 Ohm Resistor" );
pCmp[2] = new Load( 10.0, "10 Amp Load
" );
//Set topology
pCmp[0]->setF(pCmp[1]);
pCmp[1]->setB(pCmp[0]);
pCmp[1]->setF(pCmp[2]);
pCmp[2]->setB(pCmp[1]);
//Calculate currents
for( int i = numCktEleMinOne; i > -1; --i )
{ pCmp[i]->i(); }
//Calculate voltages
for( i = 0; i < numCktEle; i++ )
{ pCmp[i]->v(); }
for( i = 0; i < 3; i++ )
{ pCmp[i]->print(); }
for( i = 0; i < 3; ++i )
{ delete pCmp[i]; }
delete pCmp;
}
79
Writing Programs In
Mathematical Languages
1- this ~ keyword and special pointer



Passed as an implicit argument to class member functions
Points to object that generated call
Cannot be referenced outside of a class member function
Consider existing versions of Resistor class member functions setdT()
and calcOhms()
void Resistor::setdT( double _dT )
{ dT = _dT; calcOhms(); }
inline void Resistor::calcOhms() {
Ohms = refOhms + k * dT;
assert( Ohms > 0.0 );
}
Creating an object of type Resistor:
Resistor r1( 5.0, "5.0 Ohm Resistor",
0.0, 0.0, 0.1 );
r1.setdT( 30 );


How is the member variable dT that belongs to r1 passed to setdT() ?
How are the member variables that belong to r1 passed to calcOhms()
?
Equivalent code for setdT() and calcOhms() is:
void Resistor::setdT( double _dT )
this->dT = _dT;
this->calcOhms();
}
{
inline void Resistor::calcOhms() {
this->Ohms = this->refOhms
+ this->k * this->dT;
assert( this->Ohms > 0.0 );
}
80
Using this to output the address of an object:
void Component::address()
{
cout << "Address of "
<< szName
<< " is "
<< this
<< endl;
}
Later on it will be useful to return the object that invoked a member
function as
return *this;
Consider a global function that returns the address of an object:
void address( void *This )
{
cout << "Address of object is "
<< This
<< endl;
}
The function ::address() could be called from a member function such as
setdT() as:
void Resistor::setdT( double _dT ) {
::address( this );
dT = _dT;
calcOhms();
}
2- ?: ~ Conditional Operator

Perform same logic as if-else, but returns a value
Pseudo-code:
Operand 1 ? Operand 2 : Operand 3;
Boolean Expression ?
Execute this if true :
Code Snippets:
f( n > 0 ? x : y );
int n, m;
(k > 0 ? m : n) = 2;
return ( k > 0 ? m : n );
Otherwise execute this;
81
3
, ~ Comma Operator
binary operator
Code snippet:
int i = 2;
i = i + 1, j = i * i, k = i + j;

Evaluated left-to-right

Returns value of its right most operand
Pseudo-code
Expression 1, Expression 2,
…, Expression n;
However, simpler to write as sequence of statements
Expression 1;
Expression 2;
….
Expression n;
Consider the use in a for loop
int n,
evenSum, oddSum,
even, odd;
n = 10;
for( evenSum = 0, oddSum = 0,
even = 2,
odd = 1;
even <= n;
even = even + 2, odd = odd + 2 )
{
evenSum = evenSum + even;
oddSum
= oddSum + odd;
}
82
4-Incrementing and Decrementing Operator
Shorthand

++


~ increment operator
Unary operator
May be applied to char, int, or double types
i = 1;
i = i + 1;

prefix notation for ++
i = 1;
++i;



value of i is set to 2
expression returns value of 2
postfix notation for ++
i = 1;
i++;


value of i is set to 2
expression returns value of 1

--

prefix notation for --
~ decrement operator
i = 2;
--i;



value of i is set to 1
expression returns value of 1
postfix notation for --
i = 2;
i--;


value of i is set to 1
expression returns value of 2
83

++ and - are said to have side affects
x = y + z;
x = y++ + z;


Both values of x and y are changed
Side effects can create confusion
n = 1;
m = ++n + n;
n = 1;
m = n++ + n;
5-Shorthand Assignment Operators

May be used when a variable is the target of an assignment statement
and the variable may also appear as the first operand on the righthand side of the assignment statement





+=
-=
*=
/=
%=
k = n + k;
k += n;
k *= 2;
k /= 2;
Consider re-writing an earlier for loop
int n, evenSum, oddSum, even, odd;
n = 10;
for( evenSum = 0, oddSum = 0,
even = 2, odd = 1;
even <= n;
even += 2, odd += 2 )
{
evenSum += even;
oddSum += odd;
}
84
6- switch Statement
switch( Integral Expression )
{
case Label 1:
Block 1
break;
case Label 2:
Block 2
case Label 3:
Block 3
break;
case Label 4:
Block 4
break;
default:
Block 5
break;
}
Code snippet:
char ch;
ch = 'b';
switch( ch )
{
case 'a':
cout << "Case a" << endl;
break;
case 'b':
cout << "Case b" << endl;
default:
cout << " Default case" << endl;
break;
}
85
7-Enumeration: Programmer Defined Integral
Type
enum colors { Red, Green, Blue }
selectColors ;



colors ~ name of new type
only legal values are Red, Green, Blue
 Red = 0
 Green = 1
 Blue = 2
 Referred to as enumerators
selectColors ~ variable of type colors
Code snippet:
int n = 1;
selectColors = n; /* Compiler error */
selectColors = Red;
switch( selectColors )
{
case Red:
cout << "Red" << endl;
break;
case Blue:
cout << "Blue" << endl;
break;
case Green:
cout << "Green" << endl;
break;
default:
cout << "Unknown color" << endl;
break;
}
86
8-Code: Using Enumeration in Component
Class
//component.h
#ifndef __component_h
#define __component_h
#include <string.h>
class Component{ public:
Component( double _amps = 0.0,
double _volts = 0.0,
char _name[] = "No name") :
amps( _amps ),
volts( _volts ),
pF(0), pB(0)
{ setName( _name ); }
virtual const double& v() = 0 { return volts; }
virtual const double& i() = 0 { return amps; }
void setV( double _volts ) { volts = _volts; }
void setI( double _amps ) { amps = _amps; }
void setF( Component *pCmp ) { pF = pCmp; }
void setB( Component *pCmp ) { pB = pCmp; }
void setName( char * sz_name );
Component* f() { return pF; }
Component* b() { return pB; }
void print();
protected: double volts, amps;
enum { dim_of_name = 32 };
char
name[dim_of_name];
Component *pF, *pB;
};
#endif
//component.cpp
#include <iostream.h>
#include <assert.h>
#include "component.h"
void Component::print()
{
cout << name
<< ": "
<< "Node Volts = "
<< volts
<< " "
<< "Amps = "
<< amps
<< endl;
}
87
void Component::setName(char * _name )
{
int i = 0;
while( _name[i] ) i++;
assert( i < dim_of_name );
strcpy( name, _name );
}
9-Class friends
friend ~ global function, class member function, or an entire class may
be declared a friend of a class








Not a member of the class
Has access to all class members
Breaks class encapsulation
Should be used only when absolutely necessary
Often used with binary operator overloading
Simplify code
Use real-world notation in code
Corresponds to an overloaded function
10-Overloading the Output Operator as a
Friend
To redefine the behavior, use pattern given below:
ostream& operator<< ( ostream& output, userType& object )
{
// insert userType output
return output ;
}
88
11-Code: Overloading Output Operator for
Resistor Class
// resistor.h
#ifndef __Resistor_h
#define __Resistor_h
#include <assert.h>
#include "component.h"
class Resistor : public Component { public:
friend ostream& operator<<(ostream& output,Resistor& res);
Resistor( double _refOhms = 1.0,
char *_name = "1 Ohm Resistor",
double _amps = 0.0,
double _dT = 0.0,
double _k = 0.1 );
void setdT( double _dT );
void setOhms( double _refOhms );
virtual const double& v();
virtual const double& i();
double ohms() const;
private:
void calcOhms();
double refOhms, Ohms, dT, k;
};
inline const double& Resistor::v()
{ return volts = pB->v() - amps * Ohms; }
inline const double& Resistor::i()
{ return amps = pF->i(); }
inline double Resistor::ohms() const
{ return Ohms; }
inline void Resistor::calcOhms() {
Ohms = refOhms + k * dT;
assert( Ohms > 0.0 );
}
#endif
89
// resistor.cpp
#include <iostream.h>
#include "resistor.h"
ostream& operator<<(ostream&
{
output << "Resistor name: "
<< res.name
<< '\n'
<< "End Node Volts =
<< res.volts
<< " "
<< "Amps
=
<< res.amps
<< " \n"
<< "Ohms
=
<< res.Ohms
<< " "
<< "Delta degrees =
<< res.dT
<< " \n"
<< "Reference Ohms =
<< res.refOhms
<< " "
<< "Sensitivity
=
<< res.k
<< '\n'
<< endl;
return output;
}
output, Resistor& res
"
"
"
"
"
"
Resistor::Resistor(
double _refOhms,
char *_name,
double _amps,
double _dT,
double _k ) :
Component( _amps, 0.0, _name ),
refOhms( _refOhms ),
dT( _dT ), k( _k )
{ calcOhms(); }
void Resistor::setdT( double _dT )
{ dT = _dT; calcOhms(); }
void Resistor::setOhms( double _refOhms )
{ refOhms = _refOhms; calcOhms(); }
)
90
// Main.cpp
#include <iostream.h>
#include "component.h"
#include "resistor.h"
#include "source.h"
#include "load.h"
int main() {
const int numCktEle = 3;
const int numCktEleMinOne = numCktEle - 1;
//Obtain memory and initialize
//circuit components
Component **pCmp =
new Component *[numCktEle];
assert(pCmp);
pCmp[0] = new Source( 200.0, "200 Volt Source" );
pCmp[1] = new Resistor( 10.0,
"10 Ohm Resistor" );
pCmp[2] = new Load( 10.0, "10 Amp Load
" );
cout << *((Resistor*)pCmp[1]);
//Set topology
pCmp[0]->setF(pCmp[1]);
pCmp[1]->setB(pCmp[0]);
pCmp[1]->setF(pCmp[2]);
pCmp[2]->setB(pCmp[1]);
//Calculate currents
for( int i = numCktEleMinOne; i > -1; --i )
{ pCmp[i]->i(); }
//Calculate voltages
for( i = 0; i < numCktEle; i++ )
{ pCmp[i]->v(); }
for( i = 0; i < 3; i++ )
{ pCmp[i]->print(); }
for( i = 0; i < 3; ++i )
{ delete pCmp[i]; }
delete [ ] pCmp;
}
91
12-Overloading Operators









Special case of function overloading
All but 4 of the 52 operators may be overloaded
Operator function must be member or friend of class
Invoking object is always left-hand operand
(ie, argument)
If left-hand operand is not an object of the class type, then friend
function must be used
Only unary or binary operators may be overloaded
Unary operators may always be overloaded as class member functions
 Empty parameter list
Binary operators as class member functions
 Parameter list contains a single parameter which is right-hand
operand
Binary operators as friend functions
 Parameter list contains two parameters corresponding to left- and
right-hand operands
Pattern for operator function:
return_type class_name::operator?(argument list)
where ? is replaced with operator to be overloaded

Often return type is class for which operator is overloaded
13-Operator Overloading Restrictions
1- Cannot be overloaded: dot operator, scope resolution operator,
conditional operator
2 - Only existing operators can be overloaded
3- Number of operator operands cannot be changed
4- Operator associativity/precedence cannot be changed
5- Must have at least one user defined data type as an operand
6- Operator functions may not have default parameters
7- Operators =, [ ], (), ->, new, and delete cannot be declared as
friends.
92
14-Overloading the Assignment Operator
Code snippet:
Resistor r1( 5.0, "r1", 0.0, 0.0, 0.1 );
r1 = 10;
Resistor& Resistor::operator=( double d ){
refOhms = d;
calcOhms();
return *this ;
}
// r1.operator=( 10.0 );
Since reference is returned, expressions like
r1 = r2 = 2;
may be written.
15-Code: Another assignment operator
overload for the Resistor class
Resistor& Resistor::operator=(Resistor &r
{
refOhms = r.refOhms;
dT
= r.dT;
k = r.k;
Ohms = r.Ohms;
setName( r.getName() );
amps = r.amps;
volts = r.volts;
return *this ;
}
)
93
16-Overloading the Addition Operator
Code snippet:
Resistor
r1( 5.0, "r1", 0.0, 0.0, 0.1 ),
r2(15.0, "r2", 0.0, 0.0, 0.2 ),
rSum;
rSum = r1 + r2;
17-Code: Adding addition operator overload
to the Resistor class
Resistor Resistor::operator+( const Resistor &r ) const
{
Resistor sum;
sum.refOhms = Ohms + r.Ohms;
sum.Ohms = sum.refOhms;
sum.dT = 0.0;
sum.k = 0.0;
sum.setName( "Equivalent series ohms" );
sum.amps = amps;
sum.volts = volts + r.volts;
return sum;
}

const specifies that invoking object will not be changed by function
call
const
Resistor



R( 5.0, 0.0, 0.1, 0.0, "r1" );
Can only invoke a const member function
Member functions that do not change the value of an invoking
object should be made const
Sometimes need two versions of a member function, one that is
const and one that is not const
Consider the following addition
rSum = 3.0 + r1;
where 3.0 is to be added to the refOhms member of r1


Member function cannot be used
Friend function must be used
94
Resistor operator+( double d, Resistor &r ) {
Resistor sum;
sum.refOhms = d + r.refOhms;
sum.dT= r.dT;
sum.k = r.k;
sum.calcOhms();
sum.setName( r.getName() );
return sum;
}

Declare as a friend in the Resistor class
For resistors in parallel overload the OR logical operator:
Resistor Resistor::operator||(const Resistor &r) const {
Resistor equivalent;
equivalent.refOhms = ( Ohms * r.Ohms )
/ ( Ohms + r.Ohms );
equivalent.Ohms = equivalent.refOhms;
equivalent.dT = 0.0;
equivalent.k = 0.0;
equivalent.setName( "Equivalent parallel ohms" );
equivalent.amps = amps + r.amps;
equivalent.volts = volts;
return equivalent;
}
18-Overloading Unary Operators
Overload ++ to increment the ohmic reference value by 1
Resistor
++r1;
r1++;
r1( 5.0, "r1", 0.0, 0.0, 0.1);
/* Increments value to 6.0 */
/* Increments value to 7.0 */
95
19-Code: Adding unary operator overload to
Resistor class
Resistor& Resistor::operator++(){
refOhms += 1.0;
calcOhms();
return *this;
}


Applies to the prefix form of ++
Arbitrary choice made by language developers
To overload the postfix form
Resistor Resistor::operator++(int){
Resistor r;
r = *this;
refOhms += 1.0;
calcOhms();
return r;
}
20-Conversion Functions
Used to create casts from one type to another that the compiler will
automatically use if needed
Pattern for conversion member function prototype is
operator type();
96
21-Code: Adding conversion function to the
Resistor class
// thermal.h
#ifndef __Thermal_h
#define __Thermal_h
#include <string.h>
class Thermal {
public:
double getQ() { return Q; }
void setQ( double _Q ) { Q = _Q; }
static double getk() { return k; }
static void setk_units( double _k, char * _units )
{
k = _k;
strcpy( units, _units );
}
friend ostream& operator<<(
ostream &output, Thermal &q );
private:
double Q;
static double k;
static char units[32];
};
#endif
// thermal.cpp
#include <iostream.h>
#include "Thermal.h"
double Thermal::k = 9.478E-4;
char Thermal::units[32] = "BTUs per second
";
ostream& operator<<( ostream &output, Thermal &q )
{
output << q.units << q.Q << endl;
return output;
}
// resistor.cpp snippet
Resistor::operator Thermal() {
Thermal q;
q.setQ( Thermal::getk() * Ohms *
amps * amps );
return q;
}
97
// Circuit Model
void main() {
Resistor r1( 100.0, "100 ohm", 100.0, 0.0, 0.0 );
Resistor r2( 100.0, "100 ohm", 100.0, 0.0, 0.0 );
Resistor r;
r = r1 || r2;
Thermal q;
q = r;
// q = Thermal(R);
cout << q << endl;
}
22-Static Member Variables and Functions






Class members may be declared static
A static member variable is shared by all class objects
Static member variables must exist before any objects of the class
are created
Must be defined outside of the class declaration
Static member functions may be used to work with static member
variables
 Invoked independent of any object
 Cannot access ordinary member variables
Are inherited by derived classes
23-Destructors: Last Will and Testament









Constructors automatically called when object is built
Destructor functions automatically called when an object is
destroyed
Default destructor functions are supplied by compiler
Only one destructor per class
Destructors are generally used for objects that are using system
resources that are not released by the default destructor
Destructors are called for the following:
 Object goes out of scope
 delete is used to free memory
 Program ends
Destructor functions are executed in the reverse order of
construction
Base class destructor should be virtual if base class pointers are
used to manipulate derived class objects
Virtual destructors are different than other virtual functions in
the following two ways
 Standard naming rules for virtual functions
 All functions in a virtual destructor hierarchy are executed
98
24-Code: Adding destructor to the
Component class
class Component{ public:
Component( double _amps = 0.0,
double _volts = 0.0,
char *_name = "No name") :
amps( _amps ),
volts( _volts ),
name(0),
pF(0), pB(0)
{ setName( _name ); }
virtual ~Component() { delete [] name; }
virtual const double& v() = 0 { return volts; }
virtual const double& i() = 0 { return amps; }
void setV( double _volts ) { volts = _volts; }
void setI( double _amps ) { amps = _amps; }
void setF( Component *pCmp ) { pF = pCmp; }
void setB( Component *pCmp ) { pB = pCmp; }
void setName( const char * sz_name );
const char* getName() const { return name; }
Component* f() { return pF; }
Component* b() { return pB; }
void print();
protected: double volts, amps;
char
*name;
Component *pF, *pB;
};
void Component::setName( const char * _name )
{
if( name )
delete [] name;
name = new char[ strlen(_name) + 1 ];
assert( name );
strcpy( name, _name );
}
99
25-Copy Constructors
Consider the following code:
Resistor r1( 5.0, 0.0, 0.1, 10.0, "r1" );
Resistor r2( r1 );
Resistor Resistor::f( Resistor r )
{
Resistor sum;
sum = 1.0 + r;
return sum;
}





Defines how a copy of an object is to be made
Four default functions supplied by compiler
 Default constructor
 Assignment operator
 Destructor
 Copy constructor
A class with dynamic memory management should not use the default
functions, but should provide its own
A copy constructor is called automatically for 3 situations
 Existing object is used to initialize an object being built
 When an object is pass-by-value in a function call
 When a function returns an object the copy constructor is used to
create a temporary object that is handed back to the calling
environment
Pass-by-reference must be used for copy constructor functions
100
26-Code: Adding copy constructor to
Resistor class
class Resistor : public Component {
public:
friend ostream& operator<<(
ostream& output, Resistor& res
);
Resistor( double _refOhms = 1.0,
char *_name = "1 Ohm Resistor",
double _amps = 0.0,
double _dT = 0.0,
double _k = 0.1 );
Resistor( Resistor &R );
void setdT( double _dT );
void setOhms( double _refOhms );
Resistor& operator=(Resistor &r
);
Resistor& operator=( double d );
Resistor operator+(const Resistor &r) const;
Resistor operator||(const Resistor &r) const;
friend Resistor operator+( double d, Resistor &r );
Resistor operator++(int);
Resistor& operator++();
operator Thermal();
virtual const double& v();
virtual const double& i();
double ohms() const;
private:
void calcOhms();
double refOhms, Ohms, dT, k;
};
Resistor::Resistor( Resistor &R )
refOhms( R.refOhms
dT( R.dT ), k( R.k
Component( R.amps,
{ calcOhms(); }
:
),
),
R.volts, R.name )
101
27- try, catch, throw: Exception Handling











Exception ~ runtime error
Allows class developer to provide application programmer control
over what the user sees
try block encloses normal flow of program
if error occurs in try block, throw an exception
When an exception is thrown, program does not return to point of the
throw
 Function making throw and all try block functions are removed
from the stack
 Destructors are called for all objects created in try block up to
the throw
A catch block, or exception handler, catches object thrown
Many catch blocks may be associated with a single try block
catch blocks are placed immediately after try block
The type of exception thrown is matched against the parameter type
declared inside the catch statement parenthesis, and the catch block
where a match is found is executed
 Exceptions are caught by data type
 A default exception handler is defined by placing ellipses ( . .
. ) inside the catch statement parenthesis
 Default handler should be the last handler in a catch list
After execution of the catch block, execution continues with the
first statement past the end of all catch blocks
If no programmer defined exception handler catches an exception, it
is handled by compiler supplied default exception handler which
calls abort() function
102
Code snippet:
try{
int n;
cout << "Input value of n: ";
cin >> n;
if( n == 0 )
{
throw n;
}
double d;
cout << "\nInput value of d: ";
cin >> d;
if( d <= 0.0 )
{ throw d; }
char ch;
cout << "\nInput value of ch: ";
cin >> ch;
if( ch == '0' )
{
throw "Character cannot be set to 0"; }
double dReferenceOhms;
cout << "\nInput reference ohms: ";
cin >> dReferenceOhms;
Resistor r( dReferenceOhms, 0.0, 0.1, i, "r1" );
if( r.i() < 0.0001 )
{ throw Resistor() ; }
} /* End try block */
catch( int ) {
cout << "\n n set to 0"
<< endl;
}
catch( double d ) {
cout << "Value of d < 0"
<< endl;
cout << "Value of d = " << d << endl;
}
catch( const char *sz ) {
cout << "Exception: "
<< sz << endl ;
}
catch( ... ) {
cout << "\n No explicit handler"
<< endl;
}
With inheritance,


The handler data type may be a base class and the thrown type may be
a derived class with public inheritance
The handler type may be a pointer to a public base class, and the
thrown type a pointer to a derived class object
103
28-Nested Class Declarations and
Exception Handling




A class declaration may be included inside the declaration of
another class
Scope of embedded class is its enclosing class
An exception handling class may be implemented with an embedded
class
Member variables of an exception class should be declared with
public access so that they can be directly accessed by catch block
exception handlers
104
29-Code: Adding exception handling to the
Resistor class
class Resistor : public Component {
public:
friend ostream& operator<<( ostream& output, Resistor& res );
Resistor( double _refOhms = 1.0,
char *_name = "1 Ohm Resistor",
double _amps = 0.0,
double _dT = 0.0,
double _k = 0.1 );
Resistor( Resistor &R );
void setdT( double _dT );
void setOhms( double _refOhms );
Resistor& operator=(Resistor &r );
Resistor& operator=( double d );
Resistor operator+(const Resistor &r) const;
Resistor operator||(const Resistor &r) const;
friend Resistor operator+( double d, Resistor &r );
Resistor operator++(int);
Resistor& operator++();
operator Thermal();
virtual const double& v();
virtual const double& i();
double ohms() const;
class Error{
public:
char szError[80];
}; /* End of Error class declaration */
private:
void calcOhms();
double refOhms, Ohms, dT, k;
};
inline void Resistor::calcOhms() {
Ohms = refOhms + k * dT;
if( Ohms < 0.0 )
{
Error *pe = new Error;
strcpy( pe->szError,
"Ohms less than or equal to zero"
throw pe;
}
}
);
105
void main()
{
Component *pCmp = NULL;
double refOhms;
int error = 1;
while( error ){
error = 0;
try{
}
cout << "\n Please enter value of"
" reference ohms: ";
cin >> refOhms;
pCmp = new Resistor( refOhms, "R",
100.0, 0.0, 0.0 );
cout << "\n New Resistor defined OK \n"
<< endl;
/* End try */
catch( Resistor::Error *pe ){
cout << pe->szError << endl;
cout << "\n Reference ohms must"
" be greater than 0.0 \n";
delete pe;
error = 1 ;
} /* End catch block */
}
cout << *((Resistor*)pCmp);
delete pCmp;
}
/* end main() */
106
30-Call-by-Reference with Pointers


Addresses must be passed in function calls
Pointers must be dereferenced inside function blocks
Code snippet:
void f( int *m1, int *m2 )
{
int temp;
temp = *m1 ;
*m1 = *m2 ;
*m2 = temp;
m1 = NULL ;
}
void main()
{
int n1(1), n2(2);
f( &n1, &n2 );
cout << n1 << " " << n2 << endl ;
}
107
31-Recursive Functions

A function which calls itself

Reentrant function
 All variables are local
Consider a function for summing integers from 1 to n
int fSum( int n )
{
if( !n ) return n;
return n += fSum( n - 1 );
}
A call to fSum() is
int sum;
sum = fSum( 4 );

Telescoping function calls
n = 4 + ( 3 + ( 2 + ( 1 + ( 0 ) ) ) )

Each time function is called, new set of local variables are placed
on stack

Run-away recursive function will eventually exhaust stack

Analogy to for loop
int fSum( Initialization Code )
{
if( Boolean Expression ) return n;
return n += fSum( Update Expression );
}
108
32-Code: Circuit Model with Exception
Handling Added to the Component Class
// thermal.h
#ifndef __Thermal_h
#define __Thermal_h
#include <string.h>
class Thermal {
public:
double getQ() { return Q; }
void setQ( double _Q ) { Q = _Q; }
static double getk() { return k; }
static void setk_units( double _k, char * _units )
{
k = _k;
strcpy( units, _units );
}
friend ostream& operator<<( ostream &output, Thermal &q );
private:
double Q;
static double k;
static char units[32];
};
#endif
// thermal.cpp
#include <iostream.h>
#include "Thermal.h"
double Thermal::k = 9.478E-4;
char Thermal::units[32] = "BTUs per second
";
ostream& operator<<( ostream &output, Thermal &q )
{
output << q.units << q.Q << endl;
return output;
}
109
//component.h
#ifndef __component_h
#define __component_h
#include <string.h>
class Component{ public:
Component( double _amps = 0.0,
double _volts = 0.0,
char *_name = "No name") :
amps( _amps ),
volts( _volts ),
name(0),
pF(0), pB(0)
{ setName( _name ); }
virtual ~Component() { delete [] name; }
virtual const double& v() = 0 { return volts; }
virtual const double& i() = 0 { return amps; }
void setV( double _volts ) { volts = _volts; }
void setI( double _amps ) { amps = _amps; }
void setF( Component *pCmp ) { pF = pCmp; }
void setB( Component *pCmp ) { pB = pCmp; }
void setName( const char * sz_name );
const char* getName() const { return name; }
Component* f() { return pF; }
Component* b() { return pB; }
void print();
class Error{
public:
char szError[80];
}; /* End of Error class declaration */
protected: double volts, amps;
char
*name;
Component *pF, *pB;
};
#endif
110
//component.cpp
#include <iostream.h>
#include <assert.h>
#include <string.h>
#include "component.h"
void Component::print()
{
cout << name
<< ": "
<< "Node Volts = "
<< volts
<< " "
<< "Amps = "
<< amps
<< endl;
}
void Component::setName(
const char * _name )
{
if( name )
delete [] name;
name = new char[ strlen(_name) + 1
assert( name );
strcpy( name, _name );
}
];
111
// load.h
#ifndef __load_h
#define __load_h
#include "component.h"
class Load : public Component {
public:
Load( double _amps = 10.0,
char *_name = "10 Amp Load") :
Component( _amps, 0.0, _name ){ }
virtual const double& v()
{ return volts = pB->v(); }
virtual const double& i()
{ return amps; }
};
#endif
// source.h
#ifndef __source_h
#define __source_h
#include "component.h"
class Source : public Component {
public:
Source( double _volts = 100.0,
char *_name = "100 Volt Source" ):
Component( 0.0, _volts, _name ) { }
virtual const double& v() { return volts; }
virtual const double& i()
{ return amps = pF->i(); }
};
#endif
112
// resistor.h
#ifndef __Resistor_h
#define __Resistor_h
#include "component.h"
#include "thermal.h"
class Resistor : public Component {
public:
friend ostream& operator<<(
ostream& output, Resistor& res
);
Resistor( double _refOhms = 1.0,
char *_name = "1 Ohm Resistor",
double _amps = 0.0,
double _dT = 0.0,
double _k = 0.1 );
Resistor( Resistor &R );
void setdT( double _dT );
void setOhms( double _refOhms );
Resistor& operator=(Resistor &r );
Resistor& operator=( double d );
Resistor operator+(const Resistor &r) const;
Resistor operator||(const Resistor &r) const;
friend Resistor operator+( double d, Resistor &r );
Resistor operator++(int);
Resistor& operator++();
operator Thermal();
virtual const double& v();
virtual const double& i();
double ohms() const;
private:
void calcOhms();
double refOhms, Ohms, dT, k;
};
inline const double& Resistor::v()
{ return volts = pB->v() - amps * Ohms; }
inline const double& Resistor::i()
{ return amps = pF->i(); }
inline double Resistor::ohms() const
{ return Ohms; }
113
inline void Resistor::calcOhms() {
Ohms = refOhms + k * dT;
if( Ohms < 0.0 )
{
Error *pe = new Error;
strcpy( pe->szError,
"Ohms less than "
"or equal to zero"
throw pe;
}
}
#endif
);
// resistor.cpp
#include <iostream.h>
#include "resistor.h"
ostream& operator<<(ostream&
{
output << "Resistor name: "
<< res.name
<< '\n'
<< "End Node Volts =
<< res.volts
<< " "
<< "Amps
=
<< res.amps
<< " \n"
<< "Ohms
=
<< res.Ohms
<< " "
<< "Delta degrees =
<< res.dT
<< " \n"
<< "Reference Ohms =
<< res.refOhms
<< " "
<< "Sensitivity
=
<< res.k
<< '\n'
<< endl;
return output;
}
output, Resistor& res
"
"
"
"
"
"
)
114
Resistor::Resistor(
double _refOhms,
char *_name,
double _amps,
double _dT,
double _k ) :
Component( _amps, 0.0, _name ),
refOhms( _refOhms ),
dT( _dT ), k( _k )
{ calcOhms(); }
Resistor::Resistor( Resistor &R ) :
refOhms( R.refOhms ), dT( R.dT ), k( R.k ),
Component( R.amps, R.volts, R.name )
{ calcOhms(); }
void Resistor::setdT( double _dT )
{ dT = _dT; calcOhms(); }
void Resistor::setOhms( double _refOhms )
{ refOhms = _refOhms; calcOhms(); }
Resistor& Resistor::operator=(Resistor &r
{
refOhms = r.refOhms;
dT
= r.dT;
k = r.k;
Ohms = r.Ohms;
setName( r.getName() );
amps = r.amps;
volts = r.volts;
return *this ;
}
)
Resistor& Resistor::operator=( double d ){
refOhms = d;
calcOhms();
return *this ;
}
Resistor Resistor::operator+( const Resistor &r ) const
{
Resistor sum;
sum.refOhms = Ohms + r.Ohms;
sum.Ohms = sum.refOhms;
sum.dT = 0.0;
sum.k = 0.0;
sum.setName( "Equivalent series ohms" );
sum.amps = amps;
sum.volts = volts + r.volts;
return sum;
}
115
Resistor Resistor::operator||(const Resistor &r) const {
Resistor equivalent;
equivalent.refOhms = ( Ohms * r.Ohms )
/ ( Ohms + r.Ohms );
equivalent.Ohms = equivalent.refOhms;
equivalent.dT = 0.0;
equivalent.k = 0.0;
equivalent.setName( "Equivalent parallel ohms" );
equivalent.amps = amps + r.amps;
equivalent.volts = volts;
return equivalent;
}
Resistor operator+( double d, Resistor &r ){
Resistor sum;
sum.refOhms = d + r.refOhms;
sum.dT= r.dT;
sum.k = r.k;
sum.calcOhms();
sum.setName( r.getName() );
return sum;
}
Resistor& Resistor::operator++(){
refOhms += 1.0;
calcOhms();
return *this;
}
Resistor Resistor::operator++(int){
Resistor r;
r = *this;
refOhms += 1.0;
calcOhms();
return r;
}
Resistor::operator Thermal() {
Thermal q;
q.setQ( Thermal::getk() * Ohms * amps * amps );
return q;
}
116
Software Development Practices and
Principles
1 Experiment in Small Programs
2 Step Throught Code Line-by-Line
3 Trail-and-Error Development
4 No-Duplication Principle
Code that is used in more than one location or code that is common to
more than one type of object should be maintained in a central
location, either a function or a base class.
Attribute values that are common to many different objects should be
maintained in a common object using a "relates to" relationship.
Programs should be developed using procedural, object, and generic
modularization as approproiate.
A set of calculations should only occur once.
5 Keep-It-Simple Principle
Unless it has been definitely determined that an increase in efficiency
is needed, go with the simplest code implementation.
 Seeking efficiency often results in a more complex implementation
 Code should not be difficult to understand
 Create smaller functions
 Add more attributes
 If must go for efficiency, during testing run simple and complex
algorithms in parallel
 Use code profilers to determine where gains in efficiency may be
made
6 Objects are characterized by behavior
Download