First Steps: Inheritance
Inheriting from TNamed. TNamed as an example of an interface between the
ROOT framework and user code. Asking ROOT to find a named object. The
CINT shortcut for named objects.
As we saw in the
Stack & Heap Objects
lesson, accessing objects on the
heap
requires a
pointer
and if the pointer is on the
stack
it will get lost when it "pops off" the stack.
If that happens then access to the heap object
has been lost and we have a
memory leak
In a properly designed program this should not be a problem; object
ownership is a design issue, objects ought not to be able to "escape".
However an interactive ROOT session is not a properly designed program,
it may be nothing more that a stream of consciousness and then things can
get lost. Also, as we shall see later, ROOT may create objects that the
user wants access to. So ROOT relies quite heavily on the concept of
object naming as a way of locating objects without having pointers to
them. It does this using the
TNamed
class. Once ROOT knows about a TName object you can always find it
using a command of the form:-
MyClass *MyObject = (MyClass*) gROOT->FindObject("MyObject");
which asks the global ROOT object to find it using the name.
Suppose we want ROOT to find our Quad object how can we do it? Of
course the developers of ROOT know nothing about our Quad object, ROOT
can only find TNamed objects or objects that
inherit
from it. All we need do is make Quad inherit from TNamed which
is trivially done by changing:-
Quad.h
as shown in red
class Quad : public TNamed {
...
This means that Quad inherits publicly from TNamed; all the
public
parts of TNamed now become public parts of Quad. We can say that
Quad
IsA
TNamed object and any operation that ROOT can do on TNamed it can
now do on Quad. To demonstrate the Quad has all the properties of a
TNamed, exit ROOT if you are running it, make the above change to Quad.h,
start up ROOT, load the Quad class and create a Quad object:-
root
.L Quad.cxx
Quad* my_objptr = new Quad(1., 2., -3.);
now we will give our Quad object a name and then ask it to print
itself:-
my_objptr->SetName("amber");
my_objptr->Print();
ROOT carries over the concept of a current directory in memory from PAW.
This directory can be accessed via the global pointer gDirectory which
can be asked to add our Quad object to its list:-
gDirectory->Append(my_objptr);
then this list can be asked to print itself:-
gDirectory->GetList()->Print();
If you try this you will see that the list contains our object called
"amber". Its worth thinking just how powerful this simple demonstration
of inheritance is:-
- By inheriting from another class we can directly reuse all its code.
We are calling existing framework code, something that
happens all the time in a FORTRAN program too, although it is now more
natural. We don't have to write code within the Quad class to call the
TNamed code, all the functionality of TNamed is immediately available.
- By inheriting from TNamed, framework code can call our
code! As we saw the list that the directory owned called the objects
it contained asking them for their names. In essence TNamed provides an
interface that ROOT works to. By inheriting that interface ROOT can now
interact with the TNamed object that is embedded in Quad. There is no
parallel in a FORTRAN program for this.
This is the heart of the "paradigm shift". In procedural code, control
flows down from the top; the only way to use existing libraries is to
call the code they contain. In an OO program objects are far more
autonomous, we can create new objects to access framework ones and, by
inheriting from classes that acts as interfaces, framework objects can
call our objects.
We added TNamed to Quad so we could get ROOT to find it for us. To
demonstrate this type:-
Quad *my_ptr2 = (Quad*) gROOT->FindObject("amber");
This defines a new Quad pointer and initialises by asking the ROOT object
to find the object called "amber". The one tricky bit is the
(Quad*)
FindObject returns a
TObject
and this tells C++ to "cast up the inheritance tree" to turn it into a Quad
pointer. We can check that it really works with:-
my_ptr2->Solve();
my_ptr2->Print();
CINT has a
shortcut
that makes this even easier:-
amber->Print();
Whenever CINT encounters an identifier that it does not recognise, it asks
ROOT if it has an object with that name. If it has, it treats the
identifier as a pointer to it. However, in the case of our Quad, there
is an essential difference, if we try:-
amber->Solve();
it fails; CINT cannot find the function. To see why, we only have to
ask CINT what it thinks amber is:-
.print amber
It thinks it is a TNamed object! The reason for this is that, so far as
ROOT is concerned, all objects stored in the current directory are just
TObjects, or objects that inherit from it. So CINT asks the object what
class it really is. ROOT classes, including TNamed, have member
functions that can handle that question, but our Quad class cannot,
hence it only thinks it is a TNamed object. We know better and could
cast that pointer back to Quad. If we really want CINT and
ROOT to understand our class then we have to look at
Adding Your own Classes to ROOT
This describes an automatic system that uses CINT to generate extra member
functions for Quad so that it too can tell ROOT what class it is.
Caution
Asking ROOT to look up a name is takes time as it has to conduct a search.
Retrieving objects by name is fine for an interactive session, humans are
so slow compared to computers that efficiency is never an issue.
However, with a few special exceptions such as
ROOT I/O,
it should not be used as a way to navigate between objects in a compiled
code.
Go Back to the
The First Steps Top Page
If you have any comments about this page please send them to
Nick West