Previous Next Title Page Contents

IV More on C++
(for the interested reader)

This paragraph is intended to introduce the reader to some useful insights into C++, to allow some use of the most advanced features found in ROOT. It is in no case a full course in C++.

IV.1 C++ notions used: classes, methods and constructors

You can try to use CINT to test the given examples. Before that, exit the previous ROOT session, go to the directory "$VEGA/vegatutorial", launch ROOT/VEGA and load into the environment the tutorial macro vtutorial.C :
vega[] .L vtutorial.C
The raw command .L loads a macro into the interpreter environment. You can then use all the functions/classes/objects defined inside it.
C++ extends C with the notion of class. If you’re used to structures in C, a class is a struct, that is a group of variables that are related, which is extended with functions and routines specific to this structure (class). What is the interest? Consider a struct that is defined this way:
struct Line {
float x1;
float y1;
float x2;
float y2;
}

This structure is defined in vtutorial.C so you don’t have to define it yourself. It represents a line to be drawn in a graphical window. (x1,y1) are the coordinates of the first point, (x2,y2) the coordinates of the second point.
In standard C, if you want to effectively draw such a line, you first have to define a structure and initialize the points (you can try this):
Line firstline;
firstline.x1 = 0.2;
firstline.y1 = 0.2;
firstline.x2 = 0.8;
firstline.y2 = 0.9;

This defines a line going from the point (0.2,0.2) to the point (0.8,0.9). To draw this line, you will have to write a function, say LineDraw(Line l) and call it with your object as argument :
LineDraw(firstline);
In C++, we would not do that. We would instead define a class like this:

class TLine {
int x1;
int y1;
int x2;
int y2;
TLine(int x1, int y1, int x2, int y2);
void Draw();
}

where we added two functions, that we will call methods or member functions, to the TLine class. The first method is used for initializing the line objects we would build. It is called a constructor.
The second one is the Draw method itself. So, to build and draw a line, we have to do:
TLine l(0.2,0.2,0.8,0.9);
l.Draw();
The first line builds the object l by calling its constructor. The second line calls the Draw() method of this object. You don’t need to pass any parameters to this method since it applies to the object l and knows what are the coordinates of the line. These are internal variables x1, y1, x2, y2 that were initialized by the constructor.

IV.2 C++ notions used: inheritance and data encapsulation

IV.2.1 Inheritance

There is an obvious question that could be asked : what is the relation between a canvas and a pad ? Isn’t it almost the same thing? Yes it is. In fact, a canvas is a pad that spans through an entire window. This is nothing else than the notion of inheritance. The TPad class is the parent of the TCanvas class.
So what? The most interesting thing is that you can reuse the code written in the parent class. For example, cd() is a method that TCanvas inherits from it’s parent, TPad. Let’s try another example.
We’ve defined a TLine class that contains everything necessary to draw a line. If we want to draw an arrow, is it so different from drawing a line? We just have to draw a triangle at one end. It would be very inefficient to define the class TArrow from scratch. Fortunately, inheritance allows a class to be defined from an existing class. We would write something like :

class TArrow : public TLine {
int ArrowHeadSize;
void Draw();
void SetArrowSize(int arrowsize);
}

The keyword "public" will be explained later. The class TArrow now contains everything that the class TLine does, and a couple of things more, the size of the arrowhead and a function that can change it. The Draw method of TArrow will draw the head and call the draw method of TLine. We just have to write the code for drawing the head!

IV.2.2 Method overriding

Giving the same name to a method (remember : method = member function of a class) in the child class (TArrow) as in the parent (TLine) doesn't give any problem. This is called overriding a method. Draw in TArrow overrides Draw in TLine. There is no possible ambiguity since, when one calls the Draw() method, this applies to an object which type is known. Suppose we have an object l of type TLine and an object a of type TArrow. When you want to draw the line, you do :
l.Draw()

so Draw() from TLine is called. If you do
a.Draw()

Draw() from TArrow is called and the arrow a is drawn.

IV.2.3 Public and private : data encapsulation

We have seen previously the keyword "public". This keyword means that every name declared public is seen by the outside world. This is opposed to "private" which means only the class where the name was declared private could see this name. For example, suppose we declare in TArrow the variable ArrowHeadSize private as in :

private :
int ArrowHeadSize;
. Then, only the methods (=member functions) of TArrow will be able to access this variable. Nobody else will see it. Even the classes that we could derive from TArrow will not see it. On the other hand, if we declare the method Draw() as public, everybody will be able to see it and use it. You see that the character public or private doesn't depend of the type of argument. It can be a data member, a member function, or even a class.
For example, in the case of TArrow, the base class TLine is declared as public :

class TArrow : public TLine {
This means that all methods of TArrow will be able to access all methods of TLine, but this will be also true for anybody in the outside world. Of course, this is true provided that TLine accepts the outside world to see its methods/data members. If something is declared private in TLine, nobody will see it, not even TArrow members, even if TLine is declared as a public base class.
What if TLine is declared "private" instead of "public"? Well, it will behave like any other name declared private in TArrow : only the data members and methods of TArrow will be able to access TLine, it's methods and data members, nobody else.

This may seem a little bit confusing and the reader should read a good C++ book if he/she wants more details. Especially since besides public and private, a member can be protected... But this is another story.
Usually, one puts private the methods that the class uses internally, like some utility classes, and that the programmer doesn’t want to be seen in the outside world.
With "good" C++ practice (which we suppose is used in ROOT...), all data members of a class are also set private. This is called data encapsulation and is one of the strongest advantages of Object Oriented Programming (OOP). By doing that, nobody can see the data members of a class, except the class itself. So, from the outside world, if one wants to access those data members, one should use so called "getters" and "setters" methods, which are special methods used only to get or set the data members. The advantage is that if the programmer wants to modify the inner workings of his class, he can do so without changing what the user sees. The user doesn’t even have to know that something has changed (for the better, hopefully).
An example. In our TArrow class, we would have set the data member ArrowHeadSize private. The setter method is SetArrowSize(), we don’t need a getter method :

class TArrow : public TLine {
private :
int ArrowHeadSize;
public :
void Draw();
void SetArrowSize(int arrowsize);
}

If one wants to define an arrow, he does :
TArrow* myarrow = new TArrow(1,5,89,124);

this will call the constructor of TArrow, but also the constructor of TLine, which is the parent class of TArrow, automatically. Then, we want to set the size of the head :
myarrow->SetArrowSize(10);

and draw it :
myarrow->Draw();

IV.3 C++ notions used: operators new and delete

Why is it so interesting to use new and delete instead of malloc and free ? First, the operator new knows the size of the object it has to initialize. You don’t need to play with it. Second, and the most important, new calls the specified constructor of the class the object belongs to. No need to do special initialization, you have to think about it when you write your classes constructor.
But new also calls the constructor of all the objects that your class contains. For example, the class TPad contains a list of all objects in the pad. This list is itself an object of the class TList and there is a call to the constructor of this list in the constructor of TPad. We will see that new calls also automatically the constructor of the parent class (see the inheritance paragraph).
When you begin to use new, don’t use malloc anymore. Better said, try to avoid mixing new and malloc. There is a risk to use free() instead of delete to release a memory that was allocated by new. This is guaranteed to crash!


Damir BUSKULIC
Last update :19/11/2001;


Previous Next Title Page Contents