First Steps: Inheritance

Overview

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.

The Lesson

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:-
  1. 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.
  2. 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.

Summary


Go Back to the The First Steps Top Page


If you have any comments about this page please send them to Nick West