|
Excerpts from C++ Notes ...
An
abstract base class An
abstract base class cannot be instantiated; it is as described, abstract,
it exists only in theory. The
purpose of such a beast is to provide a template or shell description
of objects that must follow a certain guideline. In
a nutshell, as designers of the linked list data structure, we are going to say: Dear
application designer:
If you wish to add objects to my data structure, you must derive from my abstract
base class and I'm going to force you to code an comparison function
otherwise your class won't compile. In
general, an abstract base class can define any number of data or functional
members, but functional members that are not defined are labelled as null via an
= 0 declaration:
class AbstractBaseClass
{ public:
// The deriver promises to define f1()
void
f1() = 0;
};
-------------------
| X
|
|
|
| void f1() {...} |
-------------------
/
- - - - - - - - - /
|AbstractBaseClass |
|
|
- - - - - - - - - - - - - -
\
\
---------------------
|
Y
|
|
|
| void f1() {...} |
--------------------- An abstract object definition
class
Object {
public:
virtual
int compare(const Object&) = 0;
}; Suppose
a user wants to populate our data structure with InventoryRecords.
Then the user derives from Object and provides a compare function
describing how to compare InventoryRecords. class
InventoryRecord : public Object {
public:
...
int compare(const Object& o) {...}
};
-
- - - - - ---------------------- |
Object |
<--- |
InventoryRecord | |
| |
int compare(...) | -
- - - - - ---------------------- ...
A
musical instrument derivation hierachy
-----------------------------------
| Musical
Instrument
|
-----------------------------------
/
|
\ Woodwind
Brass
String |
\
/ \ /
\ |
\
/ \ /
\ Clarinet
Saxophone Trumpet Tuba
Guitar Violin
/
\
/
\
Electric Acoustic
|
|
Ibanez Samick ... Multiple
inheritance In
more complex programs, we may wish to derive a class from more than one
counterpart. An AND
condition describes multiple inheritance.
Theoretically:
---- ---
| X | | Y |
---- ---
\ /
Z is an X and a Y
\ /
------
| Z
|
------ Practically:
--------- ---------
----------
| Student | | Person |
| Person
|
--------- ---------
----------
\ /
OR
|
\ /
|
-------------------
-----------
| UniversityStudent |
| Student |
-------------------
-----------
|
|
-------------------
| UniversityStudent |
------------------- In
both cases, UniversityStudent is
a Student and a Person Actually,
the second derivation chart may be a more realistic model since we have more
concise definition of Student as Student being a Person. Regardless,
they are both examples of multiple inheritance. Memory
map of multiple inheritance To
create an AND derived relation in C++, we simple append the extra class(es)
after the colon operator separated by commas.
We could code the first model as: class
UniversityStudent : public Student,Person {
... }; Suppose
a Person has a birthDate and a countryOfBirth.
Then creating an object u of type UniversityStudent yields
something like the following in memory:
------------------- "Person"
| birthDate
| part
| countryOfBirth
|
| - - - - - - - - - |
| name
| "Student"
| marks[]
| part
|
|
------------------- All
data and functional members of Student and Person are available at the UniversityStudent
level. We can write: UniversityStudent
u(...); cout
<< u.getName() << u.average() << u.getBirthdate() <<
endl; *
Note if we opt for this design, we may decide to move the name field from
the Student class to the Person class since it makes more sense group-wise to
say that every Person has a name, birthdate and countryOfBirth. Inheritance
versus embedding Another
way to achieve the same results is to embed instances of our derived classes
inside the subclass. class
UniversityStudent {
public:
Student s; // Here, we
are saying that a UniversityStudent
Person p;
// has Student and Person counterparts
... }; This
works but the syntax is clumsier. Suppose
we want to access the Student or Person components of u, a UniversityStudent
object, UniversityStudent
u(..); cout
<< u.s.getName() << u.p.getBirthdate() << endl; IS
A is a stronger relation than HAS A, so if we recognize an IS A relation, we
should not kludge it with HAS A condition(s).
...
Exercises
(3)
Design a Dstring class which provides dynamic string management. A dynamic string can grow or shrink through the operations
provided on the class.
Dstring
s1("abc"),s2("def),s3(s1),s4; s3
= s1 + s2 : concatenates s1 to s2 and returns a
result s3
= s1.mid(4,6); : extracts 6 characters from s1 starting from the 5th character s3
= s1.left(4); : extracts the leftmost 4 characters of s1 s3
= s1.right(2); : extracts the rightmost 2 characters of s3 cout
<< s1[2]; :
extracts the 3rd character of sd s4
= s2 = s1 : assigns s4 = s1 and s3 = s2 .
You will need to a code constructor(s), a copy constructor, an assignment
operator, and a destructor. .
Allow complex numbers to be piped to/from standard input
and file streams
...
(5)
The game of chess involves pieces of various types that have various
movement patterns. The pieces are {PAWN,KNIGHT,BISHOP,KING,ROOK,
QUEEN}. All these pieces can be derived
from an abstract class called Piece. Each
piece moves differently
and can be queried for its squares of movement by calling
the function getMoves(). Here
is a sample board:
‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑
8 | BR BN BK
| W = white, B = Black
7 | BP
BB BB
| K = king, P = pawn, B =
bishop
6 | BP
| N = knight, R = Rook, Q=
queen
5 | BP
BR BP
|
4 |
WP BP
| For ex, WN at Position(B,3)
3 | WN
WR WP WP WP |
has the following squares to
2 | WB
| move to:
1 | WK
WQ
| (A,1),(C,1),(D,4),(D,2),(C,5)
‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑
(A,5)
A B
C D E
F G
H *
Note that a queen's movement can be monitored by sending a getMoves()
message to a Bishop object and a Rook object of the same
color at the same square. Class
diagram:
Piece <-- Rook
|-name
Position[] getMoves()
|-position <-- Bishop
|-colour
Position[] getMoves()
...
Position
|‑row
|‑column
Color: [WHITE,BLACK]
Board
|-8x8 array of pieces Instantiate
a board and populate it with the above sample setup. Display
the board, then let the user ask where any piece on the board
can be moved to: Sample
run: Enter
piece position => B3 White
knight at B3 can move to {A1, C1, D4, D2, C5, A5} Enter
piece position => H1 ??
No piece at H1
|