OOP’S
WEEK # 1
PRE – PROCESSOR
&
MACROS
BASIC CONCEPTS OF OOPS’s
1.Abstraction
The ability to represent a complex reality in terms of a
simplified model. Abstraction is not limited to OOP. It is
used in procedural languages in top down step-wise
refinement and through the use of subprograms, functions
etc.
2.Encapsulation
The ability to "hide" the implementation and the data
stored in an object. This protects programs from the "side-
effects" of unwanted changes
3.Inheritance
The ability to "add on" to a class, additional components
and/or to "override" certain components of the class we
are inheriting from to create a more specific model.
Suppose we created a person class. The person could be
refined to be a student by adding student characteristics
and functions. Since a person may be defined to have a
name, address, age, etc., we could now say that a student
is a person who attends a school, has a GPA, major etc.
4.Multiple inheritance
The ability to inherit and refine from more than one class.
Using the person-student inheritance example (from
inheritance above), we can also add that a person is also
an employee and has employee functions as well. In this
case the student inherits both person and employee
characteristics/functions.
5.Polymorphism
Literally "many forms", this powerful feature allows us to
have the same method take on different meanings
depending on which class is instantiated. An example
might be an abstract idea called a shape. The shape could
have two characteristics: area and perimeter. There could
be two type of shapes (inherited) called circle and square.
We would indicate in the shape class, that there are two
values to be determined, area and perimeter. The first
determining a value indicating the number of square
inches and the other inches. Since I don't know what
shape it is I cannot tell you the equation for the area or the
perimeter. I will leave those details out. When I create a
class of circles, I do know how to calculate the area and
perimeter of a circle and can now provide these details to
the appropriate functions. The same goes when I create a
class of squares. Since both circles and squares are
inherited from shapes they are considered part of the
shapes class and can be thought of as such. When I ask for
the area of a shape, through polymorphism, the type of
shape will be determined and the correct area calculated.
Polymorphism has two implementations: inherited
polymorphism, which occurs through inheritance and
overriding; ad hoc polymorphism, which occurs between
multiple class structures.
PRE-PROCESSOR DIRECTIVES
• Preprocessor Directives
• Programming with macros
The preprocessor looks at your program before it
is compiled (hence the term preprocessor).
Following your preprocessor directives, the
preprocessor replaces the symbolic abbreviations
in your program with the directions they
represent.
The #include Preprocessor
Directive
#include preprocessor directive has been used throughout this
text. The #include directive causes a copy of a specified file to
be included in place of the directive. The two forms of the
#include directive are
#include <filename>
#include "filename"
The difference between these is the location the preprocessor
searches for the file to be included. If the file name is enclosed
in angle brackets (< and >) used for standard library header files
the preprocessor searches for the specified file in an
implementation-dependent manner, normally through
predesignated directories. If the file name is enclosed in quotes,
the preprocessor searches first in the same directory as the file
being compiled, then in the same implementation-dependent
manner as for a file name enclosed in angle brackets. This
method is normally used to include programmer-defined header
files.
The #include directive is used to include standard header files
such as <iostream> and <iomanip>. The #include directive is
also used with programs consisting of several source files that
are to be compiled together. A header file containing
declarations and definitions common to the separate program
files is often created and included in the file. Examples of such
declarations and definitions are classes, structures, unions,
enumerations and function prototypes, constants and stream
objects (e.g., cin).
The #define
Preprocessor Directive:
Symbolic Constants
• The #define preprocessor directive creates
symbolic constants, constants represented as
symbols and macros operations defined as
symbols. The #define preprocessor directive
format is
#define identifier replacement-text
• When this line appears in a file, all subsequent
occurrences (except those inside a string) of
identifier in that file will be replaced by
replacement-text before the program is
compiled. For example,
#define PI 3.14159
Replaces all subsequent occurrences of the symbolic constant PI with
the numeric constant 3.14159. Symbolic constants enable the
programmer to create a name for a constant and use the name throughout
the program. Later, if the constant needs to be modified throughout the
program, it can be modified once in the #define preprocessor
directiveand when the program is recompiled, all occurrences of the
constant in the program will be modified. [Note: Everything to the right
of the symbolic constant name replaces the symbolic constant. For
example, #define PI = 3.14159 causes the preprocessor to replace every
occurrence of PI with = 3.14159. Such replacement is the cause of many
subtle logic and syntax errors.] Redefining a symbolic constant with a
new value without first undefining it is also an error. Note that const
variables in C++ are preferred over symbolic constants. Constant
variables have a specific data type and are visible by name to a
debugger. Once a symbolic constant is replaced with its replacement
text, only the replacement text is visible to a debugger. A disadvantage
of const variables is that they might require a memory location of their
data type size symbolic constants do not require any additional memory.
• The #define preprocessor directive, like all
preprocessor directives, begins with a #
symbol.
• Preprocessor directives run until the first
newline following the #.
• That is, a directive is limited to one line in
length.
• However, the combination backslash-newline
is treated as a space, not an end-of-line
marker, so you can spread the directive over
several physical lines. These lines, however,
constitute a single logical line.
IMPORTANT NOTE: Using symbolic constants in a file other
than the file in which the symbolic constants are defined is a compilation
error (unless they are #included from a header file).
EXAMPLE
/* preproc.c - - simple preprocessor examples */
#include <stdio.h>
#define TWO 2 /* you can use comments if you like */
#define OW "Consistency is the last refuge of the unimagina\
tive. - Oscar Wilde" /* a backslash continues a definition */ /* to the next line */
#define FOUR TWO*TWO
#define PX cout("X is %d.\n", x)
#define FMT "X is %d.\n"
int main(void)
{
int x = TWO;
PX;
x = FOUR;
cout(FMT, x);
cout ("%s\n", OW);
cout ("TWO: OW\n");
return 0;
}
The #define Preprocessor
Directive: Macros
In C++, macros can often be replaced by templates and inline
functions. A macro is an operation defined in a #define
preprocessor directive. As with symbolic constants, the
macro-identifier is replaced with the replacement-text
before the program is compiled. Macros may be defined with
or without arguments. A macro without arguments is
processed like a symbolic constant. In a macro with
arguments, the arguments are substituted in the
replacement-text, then the macro is expanded i.e., the
replacement text replaces the macro-identifier and argument
list in the program.
[Note: There is no data type checking for macro arguments.
A macro is used simply for text substitution.]
Consider the following macro definition with one
argument for the area of a circle:
#define CIRCLE_AREA( x ) ( PI * ( x ) * ( x ) )
Wherever CIRCLE_AREA( y ) appears in the file, the value
of y is substituted for x in the replacement text, the
symbolic constant PI is replaced by its value (defined
previously) and the macro is expanded in the program.
For example, the statement
area = CIRCLE_AREA( 4 );
is expanded to
area = ( 3.14159 * ( 4 ) * ( 4 ) );
Because the expression consists only of constants, at compile
time the value of the expression can be evaluated, and the result
is assigned to area at runtime. The parentheses around each x in
the replacement text and around the entire expression force the
proper order of evaluation when the macro argument is an
expression. For example, the statement
area = CIRCLE_AREA( c + 2 );
is expanded to
area = ( 3.14159 * ( c + 2 ) * ( c + 2 ) );
Which evaluates correctly, because the parentheses force the
proper order of evaluation. If the parentheses are omitted, the
macro expansion is
area = 3.14159 * c + 2 * c + 2;
which evaluates incorrectly as
area = ( 3.14159 * c ) + ( 2 * c ) + 2;
because of the rules of operator precedence.
Macro CIRCLE_AREA could be defined as a function. Function circleArea,
as in
double circleArea( double x ) { return 3.14159 * x * x; }
performs the same calculation as CIRCLE_AREA, but the overhead of a
function call is associated with function circleArea. The advantages of
CIRCLE_AREA are that macros insert code directly in the program avoiding
function overheadand the program remains readable because
CIRCLE_AREA is defined separately and named meaningfully. A
disadvantage is that its argument is evaluated twice. Also, every time a
macro appears in a program, the macro is expanded. If the macro is
large, this produces an increase in program size. Thus, there is a trade-
off between execution speed and program size (if disk space is low).
Note that inline functions (see Chapter 3) are preferred to obtain the
performance of macros and the software engineering benefits of
functions.
The following is a macro definition with two arguments for the
area of a rectangle:
#define RECTANGLE_AREA( x, y ) ( ( x ) * ( y ) )
Wherever RECTANGLE_AREA( a, b ) appears in the program, the
values of a and b are substituted in the macro replacement text,
and the macro is expanded in place of the macro name. For
example, the statement
rectArea = RECTANGLE_AREA( a + 4, b + 7 );
is expanded to
rectArea = ( ( a + 4 ) * ( b + 7 ) );
The value of the expression is evaluated and assigned to variable
rectArea.
The replacement text for a macro or symbolic constant is
normally any text on the line after the identifier in the #define
directive. If the replacement text for a macro or symbolic
constant is longer than the remainder of the line, a backslash (\)
must be placed at the end of each line of the macro (except the
last line), indicating that the replacement text continues on the
next line.
Symbolic constants and macros can be discarded using the
#undef preprocessor directive. Directive #undef "undefines"
a symbolic constant or macro name.
The scope of a symbolic constant or macro is from its
definition until it is either undefined with #undef or the end of
the file is reached. Once undefined, a name can be redefined
with #define.
Note that expressions with side effects (e.g., variable values are
modified) should not be passed to a macro, because macro
arguments may be evaluated more than once.