First Steps: Classes
Creating classes and objects.
So far in our lessons we have done nothing that cannot be done
(some might say better) in FORTRAN. Its now time to create our first
object. We will continue with the quadratic equation theme and turn it
into a Quad object. When starting to think of objects the first
questions to ask are:-
We will answer these as follows:-
- What concept does the object represent?
It will represent a quadratic equation.
- What internal data must it hold?
It will need the coefficients of the quadratic. The following code in
the
class statement
will define the object's data members:-
private:
Float_t fA;
Float_t fB;
Float_t fC;
The
private:
means that users of our objects cannot see the internal data; its part
of our implementation and of no concern to the object's users!
The lower case f which precedes each of the object's data members is a
naming convention used by ROOT, and adopted by MINOS, that reminds the
reader that they are looking at a "field" of the class. The term field
isn't part of C++, but is suggestive of a component of a compound
structure. Naming conventions are essential in C++, for example to
differentiate between data members and temporary variables.
- What information is needed when is it created?
Clearly we need to define information to initialise the data members.
This suggests that we need a
constructor
function of the form:-
public:
Quad(Float_t a, Float_t b, Float_t c);
This time we use public as we need users to be able to call this
function to create, or
instantiate
objects of the Quad class. This function take 3 Float_t args and these
will be used within the function to initialise fA, fB and fC. We will
define the function itself later.
- What messages can it be sent?
This is where class designers have to think really hard! The idea is to
think about the concept from all possible sides and to try to anticipate
all the ways it will be used and translate these into
member functions.
These First Steps lessons won't attempt to
discuss class design, so we will content ourselves with two:-
public:
Float_t Evaluate(Float_t x) const;
void Solve() const;
Again these are public, we want user's to use them. The first of these,
Evaluate, is passed an x value and returns a Float_t which is the
quadratic's value at that point. The const at the end is a hint to the
compiler that this particular function won't change the object's state
i.e. it's data members.
This will allow it to do extra checks when we supply the function code.
Its also useful for readers of the code, allowing them to quickly skim
over an objects member function's to see which just return information
and which change the object.
The second member function Solve will just print the roots of the
quadratic. It is not passed any arguments as shown by the empty brackets.
Unlike FORTRAN subroutines, brackets must be present even if there are no arguments.
It doesn't return anything which is indicated by the void
return value. This is a bit of a cheat, usually member functions change
an object's state and/or return values; it would be better if the object
returned its roots. However the roots are not simple Float_t values,
there could none, one or two. In other words, the roots should be
another object! To avoid making the situation too complex, we will
settle for just printing the roots.
We have now defined the interface to the Quad object and are ready to
wrap it up in a
class Quad { ... };
statement. Don't forget the trailing ; - a popular mistake for beginners!.
Create the file:-
Quad.h
containing the following:-
class Quad {
public:
Quad(Float_t a, Float_t b, Float_t c);
Float_t Evaluate(Float_t x) const;
void Solve() const;
private:
Float_t fA;
Float_t fB;
Float_t fC;
};
All the public members are collected together as a set and presented
first. This is standard practice; as users of an object only need to see
the public part, they can stop as soon as they see the private
keyword. Although it is not strictly necessary, you can check for
errors by attempting to load the header:-
.L Quad.h
All that remains now is to define the constructor function Quad and the
other two member functions Evaluate and Solve. This is done by creating
a second file called
Quad.cxx
containing:-
#include
#include
#include "Quad.h"
Quad::Quad(Float_t a, Float_t b, Float_t c) {
fA = a;
fB = b;
fC = c;
}
Float_t Quad::Evaluate(Float_t x) const {
return fA*x*x + fB*x + fC;
}
void Quad::Solve() const {
Float_t temp = fB*fB - 4.*fA*fC;
if ( temp > 0. ) {
temp = sqrt( temp );
cout << "There are two roots: "
<< ( -fB - temp ) / (2.*fA)
<< " and "
<< ( -fB + temp ) / (2.*fA)
<< endl;
}
else {
if ( temp == 0. ) {
cout << "There are two equal roots: " << -fB / (2.*fA) << endl;
}
else {
cout << "There are no roots" << endl;
}
}
}
The first thing to notice is that user headers are placed in double quotes,
unlike system ones that are surrounded by angle brackets. That's because
compilers, including CINT look in different places for the two types
of headers.
The next thing to notice is that all 3 function names are now prefixed
by Quad:: meaning that they are defined within the
scope
of the Quad class; this was implicit when the functions were declared
within the class statement.
The constructor Quad::Quad initialises the data members. Quad::Solve
returns a Float_t expression and Quad::Solve is based closely on the
quad_eqn function of the
Macros
lesson. Load the code:-
.L Quad.cxx
and if all is well we are ready to create Quad objects! The simplest way
to do this is by typing something like:-
Quad my_object(1.,2.,-3.);
This creates a Quad object called my_object and initialises it.
Its very similar to the declaration and initialisation of a built-in type:-
Int_t num = 3;
except that the assignment part is replaced by an argument list - this
argument list is past to the Quad::Quad constructor. To make the
parallel even closer, C++ permits built-in types to be initilialised the
same way e.g.:-
Int_t num(3);
Any number of Quad objects can created in a similar way.
We can ask our Quad object to calculate its roots by sending it its
Solve message:-
my_object.Solve();
We can evaluate our Quad object at some value of x, say 5.5, by
Float_t y = my_object.Evaluate(5.5);
or
cout << "Value of Quad at 5.5 is " << my_object.Evaluate(5.5) << endl;
The .print command can be used to print out the type of the object and
the values of its data members:-
.print my_object
You can also ask CINT for details of the class using the .class command:-
.class Quad
Although CINT knows about our Quad class, ROOT in general does not. If
you want ROOT to provide special services such as:-
then there is a further step that has to be taken. This involves using
CINT to build a dictionary for the class. This will not be covered in
these First Steps but further information can be found
Adding Your own Classes to ROOT
Our Quad example may seem a bit pointless, in part this is because the
example has to be so trivial but also because a program with a single
object is not an OO program. Its only when we assemble a set of objects
that start to interact does the power of OO emerge. Never the less
we have taken a first
step towards OO. We have started to collect together everything we know
about a quadratic form into a single piece of code. In principle we
could collect together the definitive knowledge and then, forever more,
when we needed a quadratic form we could reuse the code.
Of course we could do this in a FORTRAN based program too. The power of
an OO language like C++ is that a properly
engineered class becomes an extension of the language allowing the object
to be used wherever a built-in data type is allowed.
- User defined classes can be defined by creating two files e.g.:-
My_class.h - contains the class statement
My_class.cxx - contains the definition of all the member functions
and then loading using:-
.L My_class.cxx
- Any number of objects can be created using commands of the form:-
My_class my_object(arg_list);
- Messages can be sent to the objects using commands of the form:-
my_object.Message(arg_list);
- To examine objects and classes use the CINT commands:-
.print my_object
and
.class My_class
Go Back to the
The First Steps Top Page
If you have any comments about this page please send them to
Nick West