(C++ Libraries FAQ): local
[ c++.libraries.html ]
[ C++.libraries.FAQ.txt ]
(cctype): local sample
[ c++.libraries.html#cctype ]
(string): the C++ string library... TBA
Is one string inside another?
[ stringFind.cpp ]
(Course Materials for CS201 from way back): local
[ http://www.csci.csusb.edu/dick/cs201/ ]
(C++ functions): local
[ functions.html ]
(C++ inheritance): local
[ inheritance.html ]
(C++ iomanip Inpu/output Manipulators): local
[ iomanip.html ]
(C++ Linked data structures): local
[ linked.html ]
(C++ UNIX make): local
[ make.html ]
(C++ pointers): local
[ pointers.html ]
(C++ STL): local
[ stl.html ]
[ stl.algorithms.html ]
[ stl.html ]
and off site
[ http://www.sgi.com/tech/stl/ ]
(C++ templates): local
[ templates.html ]
(C++ typedef): local
[ typedef.html ]
(C++ vectors): local
[ vectors.html ]
(C++ Examples of Code): local
[ http://cse.csusb.edu/dick/examples/ ]
[ http://cse.csusb.edu/dick/cs320/c++/ ]
(C++ Glossary): local
[ c++.glossary.html ]
and
[ objects.glossary.html ]
C++ has always had a BNF-like description of its syntax since it
was C. However the precision of this description has varied
as has the language being described. Syntax summary from the draft
standard December 1996:
(C++ draft syntax):
[ gram.html ]
Latest MATHS version (based on December 1996 draft)
[ syntax.html ]
ARM version Circa 1990(C++ as updating C and giving the same value)
[ c++.syntax.html ]
The Basic Type System [ larchc++.html ]
I have some notes towards a formal semantics of C [ c.semantics.html ] that may form a basis for further work on C++.
The ISO process was completed in November 1997, but this standard will probably never have a legal online copy! ISO and ANSI needs the money made by selling paper copies of the standard.
Also see Mumit's historical STL Newbie Guide: [ STL.newbie.html ]
(C Library Reference Guide): webmonkeys???
[ index.html ]
It is even more irritating to realize that a new class you want to create is almost the same as an existing class. There new class has an extra function and one changed definition perhaps.
This happens often enough in real software for people to have invented a set of techniques for handling it. These come under the heading of "inheritance" and "polymorphism". Here are pointers to definitions:
A student is a special kind of Person - one that can take courses, can add and drop sections, has a GPA, and so on. Just like a Person a student need to be able to print itself, display itself, construct it self, destroy all its data, increase its age, change a name, change an address, ... plus doing student things like adding, dropping, passing, failing, and such.
In the old days you had to copy the Person class file and edit it to make the Student class. But this will take time and may introduce errors. Further, if something changes in the Person class (like change the year in the date of birth from 2 digits to 4 digits say) or adding a new function, forces you to duplicate the changes in the Student class.
It would be better to define the class Student so that it explicitly had a field for the personal data and #include the file containing the Person classes code (person.h), like this:
#include "person.h"
class Student {
private:
Person person;
float GPA;
//data structures pointing to sections ...
public:
//add, drop, pass, fail, ....
//copy public functions from Person
};//class StudentThe code for the functions in student mimicking a Person will be like this:
void Student::display(){ person.display(); ...}
void Student::inc_age(int years){person.inc_age(years);...}and so on.
Similarly for the other "People-like" classes: Faculty, Staff, etc.
If Person is changed by adding or deleting any functions then so must all these other classes. This is not a good use of our time.
The better solution is called "inheritance". It makes sure that a Student has a complete up-to-date Person as part of the student, but also, automagically gives the functions of Person to a Student. The code is simple we just add
: public Personto the class header of Student:
#include "person.h"
class Student : public Person {
public:
//add, drop, pass, fail, ....
private:
float GPA;
//data structures pointing to sections
};//class StudentThe above means that Student is derived from Person. Student inherits properties of a Person. Person is said to be the base of a Student.
There is no need to declare or define functions that can be done by a Person. All Students inherit them from their parent: Person. Similarly we can derive Faculty from Person. We could also derive Staff from Person. We could perhaps derive Student_Assistant from Student or from Person. Now when Person changes it as if all the other classes are updated to fit.
Inheritance is a labor-saving device.
The compiler finds the right function to apply by looking at the type of object: Person or Student or Faculty etc. If a function only appears in Person but is applied to a Student then the parents function is called. If a function is only in the Student then the compiler uses this function. If a function is declared in but Student and Person then the compiler chooses the Student's version. This is called over-riding.
Note: There is a way to ask for a slower and more dynamic implementation that is discussed later in the notes. In some object-oriented languages the functions and data have to be searched for inside the hierarchy of classes making op a particular object.
The data describing a Person is actually hidden inside the Student. In C++ the base component is placed in memory before the items added by Student.
<------------Student-------->
<---Person----><-added data->However these data items are not explicitly mentioned inside Student. There is no need to declare a Person person in Student... but the data is hidden in there just the same.
This means the address of the start of a student is automatically also the address of the start of a Person (Person's data is before the added data). So all the machine code that accesses person.name for a person could in theory also access student.name - except that no object except a bona fide Person can access a Person's data normally.
The compiler also makes sure that Peoples data can not be corrupted by deriving a new class from it. A student does not get any privileged access to its Person's data!
child | derived | subclass | specialized |
parent | base | superclass | generalized |
method | function | method | operation |
For more on the the different words used by different groups of people see [ objects.glossary.html ] and for some informal definitions see [ Informal Definitions of Terms in objects.glossary ]
class Parent{
public: //things any class can access
private: //things only Parent can access.
protected: //things that Parent and Children can access
};//end Parent
class Child: public Parent{...}
Protected data and functions are declared exactly like private and public data and functions. The word "protected" and symbolic colon(":") are placed before a list of declarations in the class that the derived class shares with its children.
This kind of diagram is not difficult to do in code if you omit the arrow heads and always put the parent above the child. [ inheritance.cc ]
Student::Student(string s, string n, string d):Person(s,n,d){GPA=0.0;}[ ex9a.cc ] The general syntax is:
Derived::Derived(formal arguments):Base(actual Arguments){added}
Notice the new use of the reserved word: "public" and the position of the colon before the "public".
class Lion { public Animal animal; ... };
class Lion public : Animal { ... };
class Lion : public Animal { ... }
class Lion :: public Animal { ... };
class Animal : public Lion { ... };Think about how this should be written and the follow this link to [ Answer to exercise ]
. . . . . . . . . ( end of section Syntax) <<Contents | End>>
. . . . . . . . . ( end of section Some Rules of Inheritance) <<Contents | End>>
class Student_assistent
: public Student, public Teacher, public Employee {
//constructors and destructors
//special features of a student assistant
};//class Student_assistentFaculty might be derived from Employee and Teacher, but not from Student.
This is called multiple inheritance. Inheriting several non-overlapping interfaces for example is simple and useful. General multiple inheritance is a controversial idea. There are conceptual problems when a class inherits overlapping members(functions and data) from different classes.
class B1 {...};
class B2 {...};
class D: public B1, public B2 {...};and both B1 and B2 have a public function f, but D does not, then you can not tell if d.f() is really
d.B1::f()or
d.B2::f()This may be spotted by the compiler... but even if it is not, it is something to be avoided.
class A {...};
class B1 : public A {...};
class B2 : public A {...};
class D: public B1, public B2 {...};then how many As are there in a D? Does each B have an A that is hidden in a D? Or is only one A hidden in D?
In C++ the default is for D to have an A associated with B1 and another distinct A associated with B2. However, it is possible for B1 and B2 to be defined so that they will share a single A whenever they are both base classes for a derived class like D:
class A {...};
class B1 : virtual public A {...};
class B2 : virtual public A {...};
class D: public B1, public B2 {...};
We can then write the interface for a set of classes once and derive the implementations from the common interface:
class interface_name {
public:
//common function prototypes
};//end interface_name
class implementation1_name: public interface_name{
private:
//private data storage for first implementation
};//end implementation1_name
class implementation2_name: public interface_name{
private:
//private data storage for first implementation
};//end implementation2_name
The implementations of the functions would be listed like this:
Type implementation1_name::function_name(arguments) {...}
Type implementation2_name::function_name(arguments) {...}The classes and functions would probably be in different header and .cc files and compiled into the library for efficiency.
A program can be written to call precisely the functions in the interface. Objects are declared using the different implementation classes. It is easy to switch between implementations - especially if you use a 'typedef' [ typedef.html ] statement to associate a name with a particular implementation class:
typedef implementation_1 my_favorite_implementation;This is like declaring a const - only we here declare our data type.
The is no problem with defining a class by combining two or more non-overlapping interfaces with some private data representing the internal state.
. . . . . . . . . ( end of section Multiple Inheritance) <<Contents | End>>
. . . . . . . . . ( end of section Inheritance) <<Contents | End>>
void * p;This means: 'p' is an address (or NULL). If non-NULL it refers to the start of some data. *p means nothing! You then manipulate the data one byte at a time. Ugly, messy and dangerous.
However, a pointer can pointer to the parent of several objects and we can be sure that it points at data of the parental type and that all the parent's operations apply - even if it actually points at an object of a derived type: The address of a student is automatically the address of a Person as well!
A Pointer can act as a surrogate or proxy for an object. This is useful because all pointers are the same size. At the machine level a pointer is just an address of a place in RAM. All addresses take op the same number of bits. As a result we can declare and manipulate arrays of pointers to objects even if (1) we are not sure what objects we are pointing at, and (2) the pointers are pointing at different types of object. We will find a neat way to use this freedom when all the different types of object are derived from a common base class.
1/2is 0 but
1.0/2has the value 0.5.
You can also overload functions - one name with different arguments.
You can even add further meanings to the operators in C++. We don't have time to do this but it is not as hard as you might think.
Suppose that and Animal is either a cat or a dog. Then we know that every animal makes a noise and needs feeding. Further we know that dogs bark and cats mew. We could encode this in C++:
class Animal { ... make_noise(){...}; name()...};
class Cat: public Animal{ .... make_noise(){ /*Meou*/}}
class Dog: public Animal{ .... make_noise(){ /*Woof*/}}
Notice that this is like (but not the same as) having two implementations of an abstract Animal.
Now this means that the compiler will recognize and compile in the correct operations in the following:
Animal a; a.name(); a.make_noise();
Dog d; d.name(); d.make_noise();
Cat c; c.name(); c.make_noise();Because Cat and Dog are both special kinds of Animals, the Animal operation on 'name()' applies to cand d as well as a.
This is the first kind of polymorphism: Operations apply to objects of different shapes that are extended from some common core. Secondly, the compiler selects the right operation for making noises for the declared type of the data to which it is applied.
Consider the following code:
Animal *p;
p=new Dog(...); // *p is a Dog but p is an Animal's address
p=new Cat(...); // *p is a Cat but p is an Animal's address
p=new Dog(...); // *p is a another DogAs the program runs the kind of Animal stored in an element in storage pointed at by p changes. Sometimes *p is a Dog and sometimes *p is a Cat... even tho' p is always an `Animal*'. So the compiler won't be able to figure out what this means:
p->make_noise();in terms of Cat and Dog. It gives up and uses
p->Animal::make_noise()whether p points at a Cat or a Dog.
To make a C++ program pick the right method (Cat or Dog) at runtime using the thing pointed at and not the pointer itself to determine what to do. This is called: dynamic dispatching. This is not as efficient as compile-time dispatching shown above. C++ provides it as a special option in a class declaration called a virtual function.
In C++ we get it by putting the word 'virtual' in front of the function declaration... like this:
class Animal { ... virtual make_noise(){...}; name()...};
class Cat: public Animal{ .... make_noise(){ /*Meou*/}}
class Dog: public Animal{ .... make_noise(){ /*Woof*/}}As the program runs the code for
p->make_noise()is more complex. It tests the type of the object that a points at and selects:
p->Animal::make_noise()
p->Cat::make_noise()
p->Dog::make_noise()depending on the type of *p.
#include <iostream.h>
class B{ public: void f(){cout <<"B"; } };
class D: public B{ public: void f(){cout <<"D"; } };
main(){
B b; D d;
b.f(); d.f(); d.B::f();
}outputs 'BDB'. So the effect of f on an object fits that object's type.
The compiler sorts this kind of polymorphism out, before the program starts running. The program (once running) has no decision to make. It has been told which function is which.
This works when there are several different derived classes.
#include <iostream.h>
class B{ public: void f(){cout <<"B"; } };
class D1: public B{ public: void f(){cout <<"D1"; } };
class D2: public B{ public: void f(){cout <<"D2"; } };
main(){
B b; D1 d1; D2 d2;
b.f(); d1.f(); d1.B::f();
d2.f(); d2.B::f();
}outputs: "BD1BD2B" -- again try this out.
Now try the following main program in place of the above:
main(){
B* p;
p = new D1; p->f(); p->B::f();
p = new D2; p->f(); p->B::f();
}It will compile because the address of a D1 (new D1) is also the start address of a B - because each D1 is a B with some added data. Similarly new D2 is also an address of a B. Thus the pointer p is polymorphic in the sense that it can point at objects of three different types: B, D1, and D2. It can even extract public data from B part of objects of these three types.
We get a surprise when we run the above code however because the indirect access to functions with -> is not polymorphic. We don't get "D1BD2B". Instead we get "BBBB".
The compiler compiles 'p->f()' without trying to find the previous assignment to 'p' - it may be determined at run time:
if(user_input.indicates_d1()) p=new D1; else p=new D2;So the compiler uses the declared type of p (B*) and deduces that this is pointing at a B, and gives us B's function.
We could program round the above. But it gets worse... suppose we have an array of items each either being a D1 or D2. We might send a pointer down the array - and we would like it to treat each item as itself not a B! The same thing happens if the D1s and D2s are in a List, Stack or Queue, or any other data structure -- we want iterators to be declared so that that they refer to the general, and yet we want the functions that apply to the thing they specifically point at.
#include <iostream.h>
class BV{ public: virtual void f(){cout <<"B"; } };
class D1V: public BV{ public: void f(){cout <<"D1"; } };
class D2V: public BV{ public: void f(){cout <<"D2"; } };
main(){
BV* p;
p = new D1V; p->f(); p->BV::f();
p = new D2V; p->f(); p->BV::f();
}The code outputs "D1BD2B" as you might expect.
Notice that only the base class (BV) has the word virtual in it.
main(){
BV *a[]={ new D1V, new D2V, new D2V, new D1V};
for( int i = 0; i < sizeof(a)/sizeof(a[0]); i++)
a[i] -> f();
}By the way, notice these other techniques in the code above:
Paul Tonning found a delightful example of this in a text book. It suggested that you could model a bowl of cereal as an array of crispies... each one either going "Snap", "Crackle", or "Pop" as defined. Applying a function to each crisp in turn gets the correct sound. Please try this. Low fat, no calories... [ rice.cc ]
You might like to think about feeding time at the zoo. Each animal makes its own noise.... and as you go round the cages.... the right noise is produced.
In other terms an abstract class can be used to express a framework that can be used in a family of related applications. By deriving a specialized version of the abstraction you can get the compiler to generate new versions of a program quickly and easily.
An abstract class has concrete classes derived from it and these can have objects. You can then treat the concrete objects as if they were also abstract ones (which they are anyway). Further - a pointer at the abstraction can actually be pointing at any of the concrete derived class's objects. One abstraction can have many different internal structures - and different performance properties.
An abstract class is more complex than an interface. It can have data and also can define the meanings of some functions while leaving others to be defined in concrete classes. The abstract class serves to organize a set of functions that are implemented in many different ways. It sets a standard that different implementations must meet. It is like a standard wall socket or jack - nothing happens unless a working device is plugged into it.
It is nice to be able to express an abstraction when we are programming a complex problem. Several object-oriented languages allow you to define an abstract class or an implementation and derive from it multiple implementations.
In C++ you (1)make all the public functions virtual. This makes sure that the correct implementation is always chosen. The compiler can sort out direct reference before the program starts. The virtual functions will figure out the rest dynamically (as the program runs). (2) make sure that it is impossible to create objects that are in the abstract class, except by declaring one of the implementations.
The syntax is odd. (1) all functions in the abstract class must be declared as virtual. (2) at least one function is declared as being equal to zero!
class Animal{
public:
virtual int legs()=0;//this is not a mistake!
virtual void feed(Food);//output a noise?
...
};The function 'legs() above is said to be a "pure virtual function'.
In Animal above the meaning of feeding an animal and the number of legs it has are deferred to a derived classes:
class Lion : public Animal {
public:
virtual int legs(){return 4};
...
};
Class Snake: public Animal{
public:
virtual int legs(){return 0;}
};
...It is now possible to declare Snakes and Lions but we can not declare an Animal. However we can declare variable that points to an Animal and make it point at a Snake or a Lion:
// Animal a; -- illegal
Snake s; // legal
Lion l; // legal
Animal * p; // legal
p=&s; //p is the address of s, *p is s.
p=&l; //p is the address of l, *p is l.
We can even declare an array of pointers to animals:
Animal * zoo[]={new Lion("Leo"); new Snake("Rattlebag"),...};
This is also an example of a moderately complex linked data structure. [ vmf.cc ]
Exercise: The main program records the marriage of a couple and the subsequent birth of twins. Suppose they have now had a boy - add commands to record suitable invented data and then test to see if it has been stored correctly.
Here are two simple examples of objects that know their own type at run time, as they change.
(RTTI):
Run Time Type Identification
[ rtti.cc ]
(SCCS):
Source Code Control System Strings - a classes that identify the version
of code that was used
[ vn.cc ]
. . . . . . . . . ( end of section More Complex Examples) <<Contents | End>>
However in some early projects, people found their compiled programs were much bigger than they expected. They found that their compiler was not using an efficient implementation of their virtual functions. This may not be a big problem with later compilers.
. . . . . . . . . ( end of section Polymorphism) <<Contents | End>>
. . . . . . . . . ( end of section Inheritance and Polymorphism) <<Contents | End>>
class Lion : public Animal { ... };(Don't forget the ';' at the end of the class!)
. . . . . . . . . ( end of section Inheritance and Polymorphism) <<Contents | End>>
. . . . . . . . . ( end of section Pointers to C++ documentation and help) <<Contents | End>>