C++ Basics for ROOT User

This is not a C++ class. It is intended to introduce basic concepts of C++ in a way that you will be able  to follow the ROOT 102 class even if you have no prior knowledege of C or C++, but are fluent in Fortran.

Why C++ Basics


ROOT as pocket calculator:

Variables, Arithmetic and Assignment Expressions, Simple Output

Example :
root [0] Int_t a;
root [1] a = 3 * 5;
root [2] cout << a << endl;
15
 
Explanation:
[0]
Declare a variable a of type Int_t
[1]
Compute 3*5 and set the value of variable a to the result
[2]
Print the value of variable a to the terminal. cout is the name of the standard output stream (in our case the terminal). The operator << passes the value of variable a converted to a string to cout. After that the endline character endl  is passed to cout to enforce the output.
Comments:
Example:
root [0] Int_t a;
root [1] a = 5.1;
root [2] cout << "a = " << a << endl;
a = 5
root [3] Double_t b;
root [4] b = 5.1;
root [5] cout << "b = " << b << endl;
b = 5.1
Explanation:
[1]
Convert the floating point value 5.1 to integer and set the value of a to the result
[2]
Send first the string "a  = ", second the value of a, third the endline character to cout
[3]
Declare a floating point variable b;
[4]
Set b to 5.1
Comments:
Example :
root [0] a = 4*5;
Warning: Automatic variable a allocated in global scope ...
root [1] a
(int)20
root [2] 4*5
(int)20
Explanation:
[0]
Compute 4*5 and set variable a to the result. CINT recognizes that the variable a is not defined. It prints a warning and declares a according to the type on the righthand side.
[1] [2]
If the semicolon after a statement is omitted, CINT prints the result to the terminal in the form (type) result. In our case the plain C integer type int is used instead of the ROOT type Int_t.
Comments:

Arithmetic and Assignment Operators

C++ Purpose FORTRAN
x++ Postincrement
++x Preincrement
x-- Postdecrement
--x Predecrement
+x Unary plus +X
-x Unary minus -X
x*y Multiply X*Y
x/y Divide X/Y
x%y Modulus MOD(X,Y)
x+y Add X+Y
x-y Subtract X-Y
pow(x,y) or  TMath::Power(x,y) Exponation X**Y (FORTRAN and CINT)
x = y Assignment X = Y
x += y (-=,*=,/=,%=,...) Updating assignment X = X+Y (X=X-Y,X=X*Y,...)
 
Comments:
Example:
root [0] Double_t x=1.3, y;
root [1] y = TMath::Erfc(x)
(Double_t)6.59920503287388940e-02
root [2] TMath::Sin(TMath::Pi())
(Double_t)1.22464679914735320e-16
Explanation:
[0]
Declare floating point variables x and y, initialize x to 1.3
[1]
Set y to the result of the function TMath::Erfc(x). There is no semicolon, therefore print the result to the terminal
[2]
Compute the value of sin(pi), no semicolon, output to terminal
Comments:

Program flow constructs:

Functions, Logical Values and Operators, Relational Operators, Control statements

Example:
file mult.C

Double_t mult(Double_t a, Double_t b) {
// multiply a and b
   return a*b;
}

root [0] .L mult.C
root [1] mult(3,4)
(Double_t)1.20000000000000000e+01

Explanation:
[file mult.C]
Define a function with name mult and two parameters (a and b) of type Double_t returning a Double_t value.
Inline comments in C++ start with // and end at the end of the current line.
The function body (here only one statement) is in curly brackets.
[0]
Load file mult.C
[1]
Execute function mult
 
Example:
file multimult.C

Double_t mult(Double_t a, Double_t b = 2) {
// multiply a and b
   return a*b;
}

Double_t mult() {
// prompt for two numbers and return the product
   Double_t a, b;
   cout << "enter a number:";        // output prompt string
   cin >> a;                         // read input
   cout << "enter a second number:"; // output prompt
   cin >> b;                         // read input
   return a*b;                       // return a*b
}
 

root [0] .L multimult.C
root [1] mult(3,4)
(Double_t)1.20000000000000000e+01
root [2] mult(3)
(Double_t)6.00000000000000000e+00
root [3] mult()
enter a number:4
enter a second number:8
(Double_t)3.20000000000000000e+01

Explanation:
[file multimult]
Define two functions with name mult, one with two Double_t parameters another without parameters. cin is the standard input stream (in our case the terminal).
The second parameter of the the first mult has a default value.
The operator >> reads a value from the standard input to the variable on the right.

[1]
Calls the mult function with two parameters
[2]
Calls the mult function with two parameters uses the default value
[3]
Calls the mult function without parameters

Comments:
Example:
file: hello.C

void hello(char *name="") {
// print hello name
   cout << "hello " << name << endl;
}

root [0] .L hello.C
root [1] hello()
hello
root [2] hello("Peter")
hello Peter
 

Explanation:
[hello.C]
Define a function with one parameter - a string named name with a default value - returning nothing.
The strange form char *name will be explained later.
Functions returning nothing must be declared as void functions. They do not need a return statement.
Comments:
Example:
root [0] TMath::Erf(.1)
(Double_t)1.12462928770574950e-01
Explanation:
[0]
Compute the value of the error function erf(x) for x=0.1
Comments:

Logical Values and Operators, Relational Operators

 
C++ ROOT extension Purpose FORTRAN
false or 0 kFALSE False value .FALSE.
true or nonzero kTRUE True value .TRUE.
!x Logical negation .NOT.X
x && y Logical and X .AND. Y
x || y Logical or X .OR. Y
x < y Less than X. LT. Y
x <= y Less than or equal X. LE. Y
x > y Greater than X. GT. Y
x >= y Greater than or equal X .GE. Y
x == y Equal X. EQ. Y
x != y  Not equal X. NE. Y
 
Remarks:
Example:
root [0] 3 < 4
(int)1
root [1] 4 < 3
(int)0
root [2] !(3<4)
(int)0
root [3] kTRUE
(Bool_t)1
root [4] kFALSE
(Bool_t)0
root [5] true
(enum bool)1
root [6] false
(enum bool)0
Explanation:
[0]
The relation expression 3<4 returns 1, i.e true
[1]
The relation expression 4<3 returns 0, i.e. false
[2]
The negation of 3<4 is of course false
[3][4]
kTRUE and kFALSE are ROOT constants to be used for logical values
[4][6]
in most C++ implementations true and false are constants for logical values
Comments:

Control statements

{ statement1 statement2 ... }

if(condition) statement

if(condition) statement1
else statement2

while(condition) statement

do statement while(condition);

for(init_expression; cont_expression; incr_expression) statement

break;

continue;

Comments:
Example:
root [0] Int_t x;
root [1] ifstream asciiFile("bevington_chapter9.data")
root [2] while(asciiFile>>x) cout << "x = " << x << endl;
x = 6
x = 1
x = 10
...
Explanation:
[1]
Open the file bevington_chapter9.data
Name its associated inputstream asciiFile.
[2]
The expression asciiFile>>x reads an integer (because thats the type of x) from the inputstream asciiFile into x. It returns 0, i.e. false if it finds the end of the corresponding file. As long as the condition returns nonzero the result is printed to the terminal.
Comments:
Example:
file: minimize.C

Double_t f(Double_t x) { return x*x; }

Double_t fprime(Double_t x) {return 2*x;}

Double_t minimize(Double_t initialGuess, Double_t desiredAccurancy) {
  Double_t x = initialGuess;
  Double_t dx;
  do {
    dx = f(x) / fprime(x);
    x -= dx;
  } while(TMath::Abs(dx) > desiredAccurancy);
  return x;
}

root [0] .L minimize.C
root [1] minimize(1,1e-10)
(Double_t)5.82076609134674070e-11
root [2] minimize(1,1e-100)
(Double_t)5.71493695641137490e-101
 

Explanation:
Minimize a function with the newton method
Comments:
Example:
root [0] Int_t sum = 0;
root [1] for(Int_t i=0; i<10; i++) sum +=i;
root [2] sum
(Int_t)45
Explanation:
[1]
The classical counting loop
init_expression:  declare i and initialize it to zero
cont_expression: continue if i is less than 10
incr_expression:  increment i by one
Comments:

Bitwise Operators

 
C++ Purpose FORTRAN
~i Bitwise complement NOT(I)
i & j Bitwise and IAND(I)
i ^ j Bitwise exclusive or IEOR(I)
i | j Bitwise or IOR(I)
i << n Bitwise left shift ISHFT(I,N)
i >> n Bitwise right shift ISHFT(I,-N)
Comments:

Variables (Objects) and Types (Classes) in more detail

A computer program is a procedure for altering the values of objects.
An object is an area of the computer's memory that has The C++ type system knows the following fundamental data types and programmer defined types named classes

C++ Fundamental Types

C++ type Size (bytes) ROOT types Size (bytes) FORTRAN analog
(unsigned)char 1 (U)Char_t 1 CHARACTER*1
(unsigned)short (int) 2 (U)Short_t 2 INTEGER*2
(unsigned)int 2 or 4 (U)Int_t 4 INTEGER*4
(unsigned)long (int) 4 or 8 (U)Long_t 8 INTEGER*8
float 4 Float_t 4 REAL*4
double 8  (>=4) Double_t 8 REAL*8
long double 16 (>= double) REAL*16
Usage hints:
see http://root.cern.ch/root/html/ListOfTypes.html for a complete list of ROOT data types

Classes

A strength of C++ is the possibility to define own data types with associated operations, socalled classes. The usage of such programmer defined types is similar to the usage of the fundamental types.

In the following example we use the ROOT class TDatime. A short excerpt from the documentation:

TDatime()
Create a TDatime and set it to the current time.

Int_t GetDate()
Return date in form of 19971224 (i.e. 24/12/1997)

Int_t GetTime()
Return time in form of 123623 (i.e. 12:36:23)

void Print(Option_t *)
Print date and time.

void Set(Int_t date, Int_t time)
Set date and time. Data must be in format 980418 or 19980418 and time in
224512 (second precision). The date must
be >= 950101.
For years >= 2000, date can be given in the form 20001127 or 1001127
internally the date will be converted to 1001127

void Set(Int_t year, Int_t month, Int_t day, Int_t hour, Int_t min, Int_t sec)
Set date and time. Year may be xx where 95 <= xx <= 99.
The year must be >= 1995.
 

Example:
root [0] TDatime now;
root [1] now.Print();
Date/Time = Wed Oct 20 14:00:29 1999
root [2] TDatime yesterday;
root [3] yesterday.Print();
Date/Time = Wed Oct 20 14:01:15 1999
root [4] yesterday.Set(now.GetDate()-1,0);
root [5] yesterday.Print();
Date/Time = Tue Oct 19 00:00:00 1999
root [6] TDatime firstApril99;
root [7] firstApril99.Set(99,4,1,0,0,0);
root [8] firstApril99.Print();
Date/Time = Thu Apr  1 00:00:00 1999
Explanation:
[0]
Declare a variable (an object) of type (class) TDatime and name now
[1]
Call the Print() method of class TDatime for object now
[2][3]
Same as [0][1] but for an object yesterday. Of course the Print methods prints the date of today.
[4]
Call the Set() method of class TDatime with two parameters for object yesterday. Set the first parameter to the result of the call to the GetDate() method of class TDatime for object now minus one, the second parameter to 0
[5]
Call the Print() method for object yesterday.
[6]-[8]
Declare a variable of type TDatime with name firstApril99, set the correct date, print the result.
 
Comments:
Example:
file: timeForLoop.C

void timeForLoop(Int_t nMax = 1000000) {
// timing of nMax iterations of a for loop doing nothing

  TStopwatch timer;

  timer.Start();
  for(Int_t i=0; i<nMax; i++);
  timer.Stop();

  cout << " Cpu time for "<< nMax << " iterations " << timer.CpuTime() << endl;
}

root [0] .X timeForLoop.C(10000000)
 Cpu time for 10000000 iterations 7.9
 

Explanation:
In the function timeForLoop an object timer of class TStopwatch is created and used to time a for loop doing nothing.
We use the methods Start(), Stop() and CpuTime of class  TStopwatch
Comments:

Derived Types

For every type (fundamental as well as for classes) there are some derived types: references, pointer and array types: The type (class) of a variable or a pointer specifies the type of objects that they can be associated with.
Example:
root [0] TDatime now;
root [1] now.Print();
Date/Time = Wed Oct 20 16:57:40 1999
root [2] TDatime & date = now;
root [3] now.Print()
Date/Time = Wed Oct 20 16:57:40 1999
root [4] now.Set(now.GetDate()-1,0)
root [5] now.Print()
Date/Time = Tue Oct 19 00:00:00 1999
root [6] date.Print();
Date/Time = Tue Oct 19 00:00:00 1999
root [7] TDatime & test;
Error: reference type date with no initialization ...
Explanation:
[0][1]
Create an object of type TDatime, call its Print() method.
[2][3]
Create an alias name date for object now, use its Print() methods. It prints the same result.
[4][5]
Change object now, Print()
[6]
date.Print() gives the same result, date and now are two names for the same object
[7]
A declaration of a reference(i.e. an alias) must specify the object for which an alias should be created, otherwise the declaration is in error.
Comments:
Example:
root [0] TDatime now;
root [1] TDatime *p;
root [2] p = &now;
root [3] p->Print();
Date/Time = Wed Oct 20 17:13:42 1999
root [4] (*p).Print();
Date/Time = Wed Oct 20 17:13:42 1999
 
Explanation:
[1]
Declare a pointer to a TDatime object, name it p
[2]
Set the pointer p to the address of object now
[3]
Call the Print() method of the object p is pointing to
[4]
*p is the object p is pointing to, so you can call the Print() method via  (*p).Print()
Comments:
Example:
root [0] Double_t x[10];
root [1] for(Int_t i=0; i<10; i++) x[i] = i;
root [2] Double_t y[10] = {0,1,2,3,4,5,6,7,8,9};
 
Explanation:
[0]
Declare an array of 10 Double_t objects with name x
[1]
Loop over the array elements
[2]
Declare and initialize an array of 10 Double_t objects with name y
Comments:
Example:
root [0] Double_t bevingtonData[60];
root [1] ifstream file("bevington_chapter9.data");
root [2] for(Int_t i=0; i<60; i++) file >> bevingtonData[i];
root [3] bevingtonData[0]
(Double_t)6.00000000000000000e+00
root [4] bevingtonData[59]
(Double_t)1.50000000000000000e+01
Explanation:
[0]
Declare an Double_t array with name bevingtonData and 60 elements
[1]
Declare an object of class istream with name file, call the constructor with one character string as parameter
[2]
Read 60 values from the stream file into consecutive elements of array bevingtonData
[3][4]
check the first and last value
Comments:
 
Example:
root [0] const Double_t pi = TMath::Pi();
root [1] pi
(Double_t)3.14159265358979310e+00
root [2] pi = 4;
Warning: Re-initialization ignored const pi ...
Explanation:
[0]
Declares pi a constant of type Double_t and initializes is to the result of the function TMath::Pi();
[1]
Uses the constant pi
[2]
Tries to change the value of constant pi
Comments:

Scope and lifetime

Scope: Where is an object known?

Lifetime: During which time is an object alive?

Example:
file countCalls.C:

Int_t countCalls() {
// return how often this function has been called
  static Int_t calls = 0;
  return ++calls;
}

root [0] .L countCalls.C
root [1] countCalls()
(Int_t)1
root [2] countCalls()
(Int_t)2
root [3] countCalls()
(Int_t)3
root [4] calls
Error: No symbol calls in current scope ...

Explanation:
The variable calls in function countCalls is declared static, it remains allocated from the first execution of that function until the end of the program. It can be used to save information from function call to function call. Without the static keyword it would be allocated and initialized to zero every time the function is called.
[4]
calls is known only in the function where it is declared. Scope: function countCalls, Lifetime: static
 
Example:
root [0] TDatime *now = new TDatime();
root [1] now->Print();
Date/Time = Fri Oct 22 09:43:39 1999
root [2] delete now;
root [3] now->Print();
Error: illegal pointer to class object now 0x0 ...
Explanation:
[0]
Declare a pointer named now to a TDatime object, initialize it with the address of a newly created TDatime object
[1]
Use the object now is pointing to
[2]
Delete the object now is pointing to
[3]
Further usage of pointer now leads to an error
Comments:
 Example:
file: fillArray.C

Double_t * fillArray(Int_t n) {
// create a double array of length n,
// fill it with random numbers
// return a pointer to it
   Double_t *x = new Double_t[n];
   TRandom generator(0);
   for(Int_t i=0; i<n; i++) x[i] = generator.Rndm();
   return x;
}

root [0] .L fillArray.C
root [1] Double_t *y;
root [2] y = fillArray(5);
root [3] for(Int_t i=0; i<5; i++) cout << y[i] << endl;
0.663086
0.669237
0.563476
0.741082
0.788386
root [4] delete[] y;
root [5] y = fillArray(5);
root [6] for(Int_t i=0; i<5; i++) cout << y[i] << endl;
0.664211

Explanation:
[fillArray.C]
x is a pointer to an array of n Double_t elements
genearator is an object of class TRandom the constructor is called with the argument 0
the array x is filled with the result of calls to the Rndm() method of object generator
fillArray returns a pointer to the newly created array

[1]
Declare a pointer y to a Double_t array
[2]
Set the pointer y to the result of a call to fillArray(5)
[3]
Output the 5 elements of y
[4]
delete the array y is pointing to
[5][6]
see [2][3]

Comments:

Function Arguments

The arguments that appear in the parameter list of a function are called formal arguments, the arguments passed to the function are called actual arguments. Each formal argument is a local variable of the function, and each formal argument is initialized with its corresponding actual argument. I.e. the arguments are passed by value instead of by reference as in Fortran.
Example:
file swap.C

void swap0(Double_t x, Double_t y) {
  Double_t temp = x;
  x = y;
  y = temp;
}
void swap1(Double_t &x, Double_t &y) {
  Double_t temp = x;
  x = y;
  y = temp;
}
void swap2(Double_t *x, Double_t *y) {
  Double_t temp = *x;
  *x = *y;
  *y = temp;
}

root [0] .L swap.C
root [1] Double_t a = 1;
root [2] Double_t b = 2;
root [3] swap0(a,b);
root [4] cout << "a=" << a << " b="<< b << endl;
a=1 b=2
root [5] swap1(a,b);
root [6] cout << "a=" << a << " b="<< b << endl;
a=2 b=1
root [7] swap2(&a,&b);
root [8] cout << "a=" << a << " b="<< b << endl;
a=1 b=2

Explanation:
[swap0]
x is a Double_t initialized with the value of the first actual argument
y is  a Double_t initialized with the value of the second actual argument
the values of x and y are swapped

[swap1]
x is an alias for the first actual argument
y is an alias for the second actual argument
the values of x and y are swapped

[swap2]
x is a pointer to Double_t initialized with the value of the first actual argument
y is a pointer to Double_t initialized with the value of the second actual argument
the values of the Double_t objects x and y are pointing to are swapped

[3][4]
the function swap0 has no effect on its actual parameters

[5][6]
the function swap1 changes the value of a and b

[7][8]
the function swap2 is called with the addresses of a and b, it does not change its arguments (the addresses)but it changes the values of a and b

Comments:

Classes:

To see how to use a class browse its header file:

MyClass.h:

class MyClass {
public:
   MyClass();
   MyClass(int x);
   MyClass(char* text);
   int GetX()
   virtual void Draw();
protected:
   SetX(int x);
private:
   int x;
};
 

Inheritance

YourClass.h:

#include "MyClass.h"

class YourClass : public MyClass {
public:
   YourClass();
   YourClass(int x, int y);
   int GetY();
   virtual void Draw();
private:
   int y;
};
 

Remarks:

There are two major advantages of inheritance: Shape *shape = new Shape[n];
shape[0] = new Triangle(...);
shape[1] = new Rectangle(...);
...
for(int i=0; i<n; i++) shape[i]->Draw();
 
 


Version 0.2: 10/22/99 Comments to Peter Malzacher