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