class Trace {
public:
Trace(char
*);
~Trace();
private:
char *
fn;
} // Trace
Trace::Trace(char * mesg)
{
cerr << "now entering: " << mesg << endl;
fn = strdup(mesg);
} // trace constructor
Trace :: ~Trace()
{
cerr <<
"now leaving: " << fn << endl;
free(fn);
} // trace destructor
Now in any function you want to trace:
int
foo(int a,int b)
{
Trace
tracer("foo");
other
stuff
} // foo
The constructor is called at the beginning
of the function and the destructor at the end. This can be expanded to
use a file member to
record tracing, read an environment variable
to see if tracing is on, keep a list of functions to be traced and only
trace those, etc.
Book has-a Publisher;
Inheritance expresses the is-a relationship.
Inheritance allows us to abstract some parts of the data to hide them
from the programmer, while allowing re-use of other parts.
For example, a product consists of one or more programs.
Programs consist of one or more code units.
Products have data like release dates, cost, part numbers, manuals, owner, completion date, etc. They have methods like is_development_complete() and get_part_number();
Programs have owners, language, compiler, completion date. They have methods like is_development_complete() and compile_program();
Code units have owners, language compilers, unit test date, completion date, NCSL. They have methods like is_development_complete() and compile_program() and count_ncsl().
You can see that there are commonalities and differences.
Here is a simpler example. The regular code file for this is here.
#include <stdio.h> #include <stream.h> #include <stdlib.h> #include <string.h> The foo class is much like the one we saw before except that the title element and some methods have been removed. class foo { public: // first we do all the public interface methods // these are used to put values in the private data elements void set_a(int x) { a=x;} void set_b(int x) { b=x;} // these are used to get the values from the private data elements int get_a() { return(a);} int get_b() { return(b);} // these are the constructors foo() { a = 0; b = 0;} // default constructor foo(int x) { a=x; b = 0;} foo(int x,int y) { a= x; b = y;} private: // these are private data, not visible outside the class int a,b; }; // class foo // class bar inherits from foo, so we only need to specify the things // that are unique to bar class bar : public foo { Using public before foo means that we want the private elements to be included in bar. Marking it private would mean we only get the public data. Almost always you will want to mark it public. foo is known as the superclass or the base class for bar and bar is known as the derived or subclass. public: void set_c(int x) { c=x;} int get_c() { return(c);} // these are the constructors bar() {c = 0;} bar(int x) {c=x;} // a and b, although inherited by bar, are still private to foo bar(int x,int y, int z) { set_a(x); set_b(y); c = z;} private: int c; }; // class bar int main(int argc, char **argv) { foo X,Y(6),Z(7,8); bar A,B(9),C(1,2,3); printf("X.a = %d X.b = %d\n",X.get_a(),X.get_b()); printf("Y.a = %d Y.b = %d\n",Y.get_a(),Y.get_b()); printf("Z.a = %d Z.b = %d\n",Z.get_a(),Z.get_b()); printf("A.a = %d A.b = %d A.c = %d\n",A.get_a(),A.get_b(),A.get_c()); printf("B.a = %d B.b = %d B.c = %d\n",B.get_a(),B.get_b(),B.get_c()); printf("C.a = %d C.b = %d C.c = %d\n",C.get_a(),C.get_b(),C.get_c()); /* here is what I get when I run this with g++ X.a = 0 X.b = 0 Y.a = 6 Y.b = 0 Z.a = 7 Z.b = 8 A.a = 0 A.b = 0 A.c = 0 B.a = 0 B.b = 0 B.c = 9 C.a = 1 C.b = 2 C.c = 3 */ /* the following statements are illegal remove the comment markers and see what your compiler does with them */ // cout << X.get_c() << endl; // cout << A.c << endl; /* here are the errors I get inh.cc: In function `int main(int, char **)': inh.cc:67: no matching function for call to `foo::get_c ()' inh.cc:68: member `c' is a private member of class `bar' */ } // mainNote the public before bar. C++ defaults to private access to the superclass, which means we don't have access to the private members of the superclass. there are two kinds of inheritance access.
private | public | |
private | private | private |
protected | private | protected |
public | private | public |
bar(1,2,3);before the 3 arg constructor is executed, the no arg constructor for foo is run. Then the calls to set_a() and set_b() override the initialization. This is a little redundant. so we can define the subclass constructor to use the parameterized superclass constructor.
foo(const int X,const int Y, const int Z) : foo(X,Y) { c=Z; }If there were more superclasses, make a comma separated list. This can be used to initialize variables by calling a constructor.
point() : x(0),y(0) {}; point( const int X,const int Y) : x(x),y(y) {};
class foo {
public: // first we do all
the public interface methods
// these are used to put values in the private
data elements
void set_a(int x) { a=x;}
void set_b(int x) { b=x;}
// these are used to get the values from the
private data elements
int get_a() { return(a);}
int get_b() { return(b);}
int bigger();
// these are the constructors
foo() { a = 0; b = 0;} // default
constructor
foo(int x) { a=x; b = 0;}
foo(int x,int y) { a= x; b = y;}
private: // these are private data, not
visible outside the class
int a,b;
}; // class foo
int
foo::bigger()
{
if(a < b) return(b);
return(a);
} // foo::bigger
// class bar inherits from foo, so we only need to specify the things
// that are unique to bar
class bar : public foo {
public:
void set_c(int x) { c=x;}
int get_c() { return(c);}
int bigger(); // same name as one in foo
// these are the constructors
bar() {c = 0;}
bar(int x) {c=x;}
// a and b, although inherited by bar, are still
private to foo
bar(int x,int y, int z) { set_a(x); set_b(y);
c = z;}
private:
int c;
}; // class bar
int
bar::bigger()
{
if(get_a() < c) return(c);
return(get_a());
} // bar::bigger
int
main(int argc, char **argv)
{
foo X,Y(6),Z(7,8);
bar A,B(9),C(1,2,3);
printf("X.a = %d X.b = %d\n",X.get_a(),X.get_b());
printf("Y.a = %d Y.b = %d\n",Y.get_a(),Y.get_b());
printf("Z.a = %d Z.b = %d\n",Z.get_a(),Z.get_b());
printf("A.a = %d A.b = %d A.c = %d\n",A.get_a(),A.get_b(),A.get_c());
printf("B.a = %d B.b = %d B.c = %d\n",B.get_a(),B.get_b(),B.get_c());
printf("C.a = %d C.b = %d C.c = %d\n",C.get_a(),C.get_b(),C.get_c());
// now lets play with bigger()
printf("C.bigger = %d \n",C.bigger());
printf("Z.bigger = %d \n",Z.bigger());
printf("C.foo::bigger = %d \n",C.foo::bigger());
/* here is what I get when I run this with g++
X.a = 0 X.b = 0
Y.a = 6 Y.b = 0
Z.a = 7 Z.b = 8
A.a = 0 A.b = 0 A.c = 0
B.a = 0 B.b = 0 B.c = 9
C.a = 1 C.b = 2 C.c = 3
C.bigger = 3
Z.bigger = 8
C.foo::bigger = 2
*/
} // main
class instrument {
public:
void play(int
note) { cout << "instrument::play" << endl;}
}; // instrument
class wind : public instrument {
public:
void play(int
note) { cout << "wind::play" << endl;}
}; // wind
void tune(instrument& i)
{
i.play(1);
}
main()
{
wind flute;
tune(flute);
} // main
This prints instrument::play. This is because the variable in
tune() is of type instrument so it uses the function from there, even though
we
passed in a flute. Change the definition in instrument
to:
virtual void play(int note) { cout << "instrument::play" << endl;}
Now running the program prints wind::play. The function to be called is determined at run time (late binding). This technique allows us to write functions like tune() that will work for any derived class from a base class. It is easier to extend the classes because tune() still works for other classes derived from instrument. Like drum or guitar. It will also work for lower levels. If class brass is derived from wind, tune() still works. If there is no overridden functions, the one next up the hierarchy is used.
throw classname(args);
This creates
an instance of this class and effectively returns it from the function,
even if that isn't the return type of the function.
Usually the
context of thrown errors is a try block. The code looks like:
try {
// some code that generates errors
}
catch(thrown_type1 var) {
// catcher code
}
catch(thrown_type2 var) {
// other catcher code
}
Only one catch
clause is needed for each exception type, even if there are multiple throw
statements in the try block. C++
supports the
termination model. The assumption that the code throws an exception because
things are so bad there is no help. If
you want to
construct the catchers so they try to fix the problem, put the try block
inside a while loop.
exception specification
To tell the
user what the function is likely to throw as exceptions, there is an extention
of the function definition syntax.
void foo() throw( toobig,badpointer);
A function definition without a throw clause indicates that any exception can be thrown.
void foo() throw();
Means hat no
execptions will be thrown. Specifying the execeptions helps the user know
what to do if they want to put a try loop
around your
code. generic catchers can be used.
catch(...) {
}
This should be
put at the end of the catcher list to pick up any you missed. If you can't
figure out what to do, putting a call to
throw with no
arguments in a catcher will cause the exception to be re-raised to be picked
up in the next outer try block. To make
sure everything
is clean, when you leave the scope of the code that threw the exception,
all objects that have been succesfully
constructed,
are destructed.
When to use
Use them for
non-local errors. If you can handle it, do it. Use heirarchies of execptions
to help classify the error. use reference
parameters in
catchers. Wrap a simple try block with a generic catcher around code and
refine it as needed. Good to throw
exceptions during
construction so execution doesn't continue with incomplete code. Don't
use exceptions in destructors. scope
changes call
destructors and you might get in a loop.