Overview
STREAMS
The stream i/o class hierarchy is shown below
Streambuff is the base class and all the derived classes have inherited its characteristics.
At the most basic level :
cin.get(letter);
... will take a letter from the input stream (probably the keyboard) and put it into the variable 'letter'. This has replaced the C getche() function.
cout.put(letter);
... will send letter to the output stream (probably the screen)
There is a simplified shorthand way of using these commands:
cin >> letter;
cout << letter;
cout << 5 + 5; // will calculate 5+5 then display the result
To read a whole line of text from the keyboard...
cin.getline(TextString,80,'\n');
... will read upto 80 characters or until enter is hit. The text will be stored in a string called TextString.
THE IOS CLASS
The ios class offers several extra features:
| flags rdstate precision |
setf eof clear |
unsetf fail tie |
width good |
fill bad |
Not sure why these are described as features - they're not!
The class istream offers these features:
| get peek read |
putback getline seekg |
gcount tellg |
ostream offers
| put write |
seekp tellp |
The ios class has format flags
flag meaning skipws skip whitespace on input left left adjust on output right right adjust internal pad after sign or base indicator dec decimal oct octal hex hex showbase show integer base showpoint show decimal point & trailing zeros uppercase uppercase hex characters showpos + before positive numbers scientific scientific notation fixed floating notation unitbuf flush o/p after each operation stdio flush o/p after char inserted
EXAMPLE
int number = 45; cout.setf(ios::hex | ios::showbase | ios::uppercase); cout << number << endl;
output
0X2D
(i.e. the value is in hex, base is shown, the hex characters are uppercase. The endl is a way of adding a newline to the end of a line)
endl while it will give a return - this is probably the glibbest description of what end does!
The output can be reset using the unsetf function:
cout.unsetf(ios::uppercase);
will no longer show hex characters in uppercase.
EXAMPLE
double pi = 3.141592654;
cout.width(12);
cout.precision(4);
cout.fill('!');
cout << pi << endl;
will look like this:
!!!!!!3.1415
MULTIPLE INHERITANCE
This is when a new class will inherit the characteristics of more than one existing class:
class c : public a, public b
{
// new class declarations
};
New class 'c' has inherited both class 'a' and class 'b' characteristics.
VIRTUAL BASE CLASSES
Consider a class heirarchy where a base class 'A' is inherited by two classes 'B' and 'C'. A fourth class 'D' has the characteristics of both 'B' and 'C'
class a
{
public:
int x;
};
class b : public a
{
public:
int y;
};
class c : public a
{
public:
int z;
};
class d : public b, public c
{
public:
void PrintNums() { cout << x << " " << y <<" " << z << "\n"; }
};
void main()
{
d d_ex;
d_ex.x = 5;
d_ex.y = 6;
d_ex.z = 7;
d_ex.PrintNums();
}
The above code will not work. The compiler can see two ways to get to variable d_ex.x
The problem is resolved by adding the keyword 'virtual' to the declarations of 'b' and 'c':
virtual is not a keyword - it's a modifier
class b : virtual public a class c : virtual public a
EXPLANATION
The choice of which overoaded function to use is made at compile time. This is known as 'early binding'. Choice of virtual code is made at run time i.e. 'late binding'. In the above example, the user need not worry which class ('b' or 'c') the class 'a' is accessed through. Only one path will be taken. The unused class will not exist (since it is virtual) so no ambiguity exists.
Late binding is slower to execute than early binding.
POINTERS TO STRUCTURES
Way way back, many moons ago, we had K & R and it did us well for a long long time..., stdio.h was happy and main was an implicit int. Then things moved on... main *had* to return an int and in C++ land, the C headers could be safely used by adding "c" to before the header name and dropping the .h - not whoever wrote this had noticed....
It's worth noting as well that the practice of placing things on the stack is rather dangerous for beginners. new has been covered by now, so why not use it?
#include <stdio.h>
main()
{
struct shopping
/* define structure containing 2 integers */
{
int ItemOne;
int ItemTwo;
};
struct shopping list[100], *MyPtr;
/* list is an array of type shopping */
/* MyPtr used to point to a type shopping */
MyPtr = &list[34]; /* point to where list[34] is */
MyPtr -> ItemOne = 16; /* access ItemOne within list[34] */
printf("%d\n",MyPtr->ItemOne); /*print what MyPtr points to */
}
Summary:
struct shopping list[100],*MyPtr;
means the MyPtr points to an array, each box of which has a structure in it.
MyPtr = &list[34];
means MyPtr points at box 34
MyPtr->ItemOne = 16;
means point at ItemOne with the structure in box 34 and set ItemOne to be 16.
POINTERS TO CLASS HEIRARCHIES AND ASSOCIATED PROBLEMS
Pointers can be used to point to class memebers in the same way that structure members can be accessed:
#include <iostream.h>
class parent
{
public:
void print(void) { cout << "Parent function\n"; }
};
class son : public parent
{
public:
void print(void) { cout << "Son function\n"; }
};
class daughter : public parent
{
public:
void print(void) { cout << "Daughter function\n"; }
};
void main(void)
{
parent p; // p of type parent
son s; // s of type son
daughter d; // d of type daughter
parent *pptr = &p;
son *sptr = &s;
daughter *dptr = &d;
pptr->print(); // prints : Parent function
sptr->print(); // prints : Son function
dptr->print(); // prints : Daughter function
pptr = &d; // pptr now points a 'd'
pptr->print(); // STILL prints : Parent function
pptr = &s; // pptr now points at 's'
pptr->print(); // STILL prints : Parent function
}
This isn't a bad example - but it completely lacks any explaination as to why when pptr points at "s" or "d", it still outputs the "p" parent function
EXERCISE 19
Construct a class that amongst it's data members, contains a pointer to a simple variable. Declare an object of the class and a pointer that points to it. Use the -> indirect member selector operator to access the data members in the class.
SUMMARY
If a pointer is used to point to a base class object (e.g. pptr pointer pointing to parent), the other pointer remains pointing at the base class members and can only be used to access the base class members.
HOW TO OVERCOME THESE PROBLEMS AND POLYMORPHISM
This problem is overcome by defining the member function of the base class 'virtual':
virtual void print(void) { cout << "Parent function\n"; }
This way the pointer can be used to access the redefinition of the function in a derived class.
Instances of redefined functions do not need the virtual keyword. However it is still good practice to define them as virtual:
virtual void print(void) { cout << "Son function\n"; }
virtual void print(void) { cout << "Daughter function\n"; }
This means that virtual functions can be identified without reading the whole program.
There is no such function in C called getche() - ho hum....