C++ For Physicist
C++ For Physicist
I N D I A - B A S E D N E U T R I N O O B S E R V AT O R Y
Contents
39
File I/O
45
Classes
51
11
17
25
31
65
77
35
87
99
103
81
Introduction
This guide was prepared as an aid for the graduate students of the
India-based Neutrino Observatory programme at the Tata Institute of
Fundamental Research, Mumbai. I will begin by giving an overview
of what happens inside a computer when a program is run and then
making you acquainted with the tools like required to write and run
a program and then the ways to use the tools. Therefore, I might
introduce some concepts in the first few classes which you might not
understand. These concepts will become clearer as we progress but
they are necessary to explain how the tools work.
I feel that, swimming and programming languages should not be
taught using the black board alone. The audience will understand everything that is taught in that way but when pushed into real waters,
they are very likely to drown. This comes from my personal experience of learning C++. Therefore we will jump right away into the
real waters, holding the side walls of the pool we shall learn first how
to float and then how to swim. This will make the classes interesting
right from the start and less time consuming as well. We will learn
from our mistakes by deliberately introducing bugs in our program.
This way you will be ready for handling such things when you start
programming for your research work. No program is perfect-Even
the operating systems that we use give us security updates everyday. Therefore do not worry if your program crashes but strive to
make it better with every update. I would appreciate if you come
along with your laptop (i.e., the swimming pool) to my classes. You
will enjoy the classes as you start understanding the power of the
C++ language.
B
0
1
0
1
S
0
1
1
0
C
0
0
0
1
8085 microprocessor:
Mnemonic
ADD
CALL
CC
CMP
Operation
ADD
Call unconditional
Call on Carry
Compare with Memory
For example to add two numbers, you have give the hexadecimal
code 87 to the processor input which will suitably switch and connect the gates so that an adder is formed and then the address of
the locations of the two numbers to be added are given. So A and B
are connected to these memory locations. The output is also connected to a memory location but we need not get into details of that
for now. 1
Programming using the instruction set is usually called Assembly Level Programming. This is also called Low level language/programming. Programs written in low level languages are
faster in some sense. However, there are inherent disadvantages in it.
One writes a mnemonic code and then replaces them by the binary
codes and then checks if the codes are right and then checking if the
program runs properly. This is not an efficient way to develop a code.
Programmers nowadays use high level languages which remove (in
fact, hide) all the complexities involved in putting together the pieces
in the assembly language. High level languages give an advantage
to a developer to write a code by using natural and understandable
expressions.2
Examples of High Level Languages: C, C++, Java etc., High level
languages classified into two types3 :
Compiled Languages: Here there is an intermediate converter
called a compiler which converts the statements in your program
into binary instructions that the processor understands, apart from
checking your code for possible syntax errors. Examples are C,
C++ etc.,
Advantage: Speed of execution
Disadvantage: Portability-You need to recompile your code on
each machine it runs.
Interpreted Languages: Each statement in the code and then the
corresponding instruction is converted (using a java virtual machine (JVM) for Java) and executed one after the other. Examples
are Java, Python etc.,
Advantage: Portability-The same Java code will run in any machine and in any platform where JVM is installed.
Operating Systems
The operating system is the manager of your computer. It is also
another program written in C (well, at least Linux). It manages and
controls the use of various resources in your computer. Have a look
at the Linux kernel in the figure given below. The bottom block is
your hardware and most of the upper blocks are software pieces
which build the operating system.
Figure 2: The pieces of codes that make
a typical operating system. The most
famous OSs are Windows, Linux and
Apple. Linux is available for free (you
can download the source code of Linux
and see what is inside) and Ubuntu is a
flavor of Linux.
10
#include <iostream>
int main ()
return 0;
}
main.cpp: Hello World
#include <iostream>
int main ()
int a=99;
int b=100;
8
9
}
main1.cpp: Adding two numbers
#include <iostream>
return (x+y);
int main ()
int a=99;
12
10
int b=100;
11
12
return 0;
13
}
main2.cpp: Adding using a function
The use of functions will not be obvious in simple cases like these but
you will see the use of functions in almost all the C/C++ programs.
Though both the programs yield the same answer, functions give you
more flexibility as they stand out as a separate entity.
#include <iostream>
int main ()
int a=99;
int b=100;
return 0;
10
11
12
return (x+y);
13
To make sure that the compiler knows what Add means, the function should be ideally defined before the main function but this
method has its own disadvantage. Imagine two functions that call
each other-you will obviously fail in trying to decide which function to be placed first. The method of function prototyping is used in
such cases to declare a function so that the compiler knows what the
function looks like but does not know what the function does. For
example, the prototype of the Add function is done in a single line
like:
13
Declaring a function before the main function in order that the compiler is aware of a function is called Forward Declaration. Using
forward declarations, you now have the freedom of writing your
function definitions anywhere you want. An example is shown below.
1
#include <iostream>
int main ()
int a=99;
int b=100;
return 0;
10
11
12
13
return (x+y);
14
\\Add.h
//Add.cpp
return (a+b);
}
Add.cpp: Addition function source file
//main.cpp
#include <iostream>
14
int main ()
int a=99;
int b=100;
10
return 0;
11
}
main3.3.cpp: Compiling multiple files
15
\\Sub.h
\\Sub.cpp
return (a-b);
Now we shall create an object (Sub.o) for this file using the command
described earlier:
16
\\main.cpp
#include <iostream>
int main ()
int a=99;
int b=100;
10
cout<<Add(a,b)<<endl;\\sum of a and b
11
cout<<Sub(a,b)<<endl;\\diff of a and b
12
return 0;
13
}
main5.cpp: usage of libraries
int a=99;
The above statement not only allocated memory but also set the bits
in that allocated memory space to the corresponding value. So when
the term memory comes, bring the picture shown here to your mind.
Though the value of the variable can be changed, the address cannot
be altered. You can only read the address of a variable using the &
operator. The following code prints the address of the variable a and
the date stored in that address i.e., the value of a.
1
#include <iostream>
int main ()
int a=99;
int b=100;
return 0;
10
}
main6.cpp: Address of a variable
18
Name
char
short
int
long
bool
float
double
long double
Description
character (1 byte)
short integer (2 bytes)
integer (4 bytes)
long integer (4 bytes)
boolean value-true/false (1 byte)
floating point (4 bytes)
double precision float (8 bytes)
long double precision float (8 bytes)
like int as an unsigned type where the numbers are treated as having a positive value. Therefore if the range of an int type is from
-2147483648 to 2147483647, the range for the unsigned int will be
from 0 to 4294967295. The statements for declaring a unsigned types
look like:
unsigned int a;
unsigned long b;
unsigned float c;
Do it yourself:
Try subtracting two unsigned integers a and b. First try a-b and
then b-a. What is the problem here?
Escape characters
There are some special characters that are used in C/C++ which have
a special meaning especially in a cout statement. They are usually
not printed. They are listed in Table 2.
Lets write a simple program that takes an input from the user and
gives the mean as the output using the concepts that we learnt so far
and in a interactive manner. The cout command is used to print
out a statement while the cin command accepts a user input. These
commands are useful for an interactive program and are illustrated
in the example below.
19
Escape code
\n
\r
\t
\v
\b
\f
\a
\
\"
\?
Description
newline
carriage return
tab
vertical tab
backspace
form feed (page feed)
alert (beep)
single quote ()
double quote (")
question mark (?)
#include <iostream>
return (x+y)/2;
int main ()
float a;
10
float b;
11
12
cin>>a;
13
14
cin>>b
15
16
return 0;
Scope of a variable
In the above code, the variables a and b are seen only by the main
function and therefore they have a local scope. If instead for some
reason, you want the function mean to also see these variables,
you can declare outside main before the mean function. Then these
variables are said to have global scope.12
1
#include <iostream>
float a;
float b;
20
cout<<a;
return (x+y)/2;
10
int main ()
11
12
13
cin>>a;
14
15
cin>>b
16
17
return 0;
main8.cpp: scope of a variable
Do it yourself:
Check what happens if the float function in the above example
takes int parameters instead of float.
What happens if in the above code, a and b are once again declared inside the main function along with those outside?
Assignment Operator
The most basic operator is the assignment operator =. This assigns
a particular value to the variable. For example, the expression:
int a=5;
assigns a value of 5 to the variable a.
Mathematical Operators
Mathematical operators are the most frequent ones that you will
use in a program, especially if you are a physicist. They are listed in
Table 3
Operator
+
?
/
%
Description
Addition
Subtraction
Multiplication
Division
Modulo
21
int a=100;
a++;\\a=101
int a=100;
a--;\\ a contains 99
int a;
int b=99;
Here, after the last expression, the value of a is set to 99 and the
value of b is set to 100. That is, the value of b is incremented after the
assignment operation. If the increment operator is used as a prefix:
1
int a;
int b=99;
the value of a and b are set 100. That is, the value of b is incremented
before the assignment operation.
22
Relational operators
These operators compare the values of two expressions and return
a boolean value (true or false). The result of various relational operations for the variables a and b after the following expression is
tabulated in Table 4:
int a=100;
int b=99;
Relational Operator
==
!=
>
<
>=
<=
Description
Expression
Return
equal to
not equal to
greater than
lesser than
greater than or equal to
lesser than or equal to
(a==b)
(a!=b)
(a>b)
(a<b)
(a>=b)
(a<=b)
false
true
true
false
false
false
Logical operators
These operators perform logical operations on expressions that return
a boolean value. The result of logical operations for the variables a
and b after the previous expression is tabulated in Table 5:
Logical Operator
!
&&
||
Description
Expression
Return
logical inversion
logical AND
logical OR
!(a>b)
(a>b)&&(b<1000)
(a<b)||(b<50)
false
true
false
Conditional Operator
The conditional operator ? evaluates an expression and returns one
value if it is true and returns another value if it is false. The synatax
is:
condition ? result1 : result2
The use of this operator is illustrator is explained in the following
code.
1
#include <iostream>
23
int main ()
int a,b,c;
a=100;
b=99;
c = (a>b) ? a : b;
10
c = (a<b) ? a : b;
11
12
return 0;
13
}
main9.cpp: Conditional operator
The first cout statement will print out 100 since a is greater than b.
The second one will print out 99.
Bitwise operators
These operators modify the bit patterns of a variable. They are listed
in the following Table 6.
Bitwise Operator
&
|
Description
Expression
Return
AND
OR
XOR
NOT
Shift Right
shift Left
0x01&0x01
0x01|0x01
0x01|0x01
0xFFFFFFFF
0x011
0x011
0x01
0x01
0x00
0x00
0x00
0x02
Typecasting
There are some data types which can be converted into other types
using the typecasting operator. A typical example is given below:
float pi=3.14;
int i = (int)pi;
The value of i is now 3 and the numbers after the decimal point is
lost. This is a trivial example and as you can also check that the value
pi can be directly assigned to i without typecasting and it still
works the same way. This is because the compiler takes the freedom
of typecasting is some cases. Typecasting will become more obvious
when you start dealing with pointers.
Do it yourself:
What is the result of the expression int a= 100 + 2 * 5 ?
24
Is it 110 or 510?
Learn more about operator precedence.
Control Structures
Often in your program, you may have to taken decisions and route
your program to a particular segment or repeat a particular segment
of code. These are done by control structures which are discussed in
this chapter.
#include <iostream>
int main ()
int a=99;
int b=100;
if (a<b)
{ \\ begin braces
9
10
} \\ end braces
11
else
12
{ \\ begin braces
cout << "a is greater than b";
13
14
} \\end braces
15
return 0;
16
}
main10.cpp: if else structure
The above code prints out a is less than b as the condition a<b
satisfied. But this code will fail if the values of a and b are the same.
First it checks if a<b and since it fails and therefore it goes into the
else structure to print out a is greater than b which is wrong. In
such cases, you can use a combination of if and else using the
else if keyword.
26
if (a<b)
{ \\ begin braces
cout << "a is less than b";
3
4
} \\ end braces
else if (a>b)
{ \\ begin braces
cout << "a is greater than b";
7
8
} \\end braces
else
10
{ \\ begin braces
cout << "a is equal to b";
11
12
} \\end braces
main11.cpp: else if structure
while loop
The while loop is repeated until the condition checked by the while
statement is satisfied as shown below:
1
#include <iostream>
int main ()
int a=0;
while (a<100)
{ \\ begin
8
9
10
} \\end
11
12
return 0;
main12.cpp: while loop
27
#include <iostream>
int main ()
int a=0;
do
{ \\ begin
a++;
10
} \\end
11
while(a<100);
12
13
return 0;
main13.cpp: do while loop
for loop
The for loop is also similar to the while loop but with extra functionalities. Unlike the simple while statement, there is an initialization
part, conditional part and a statement which is executed in the for
statement.
1
#include <iostream>
int main ()
{ \\begin
cout << a << ", ";
7
8
}\\end
10
11
}
main14.cpp: for loop
The code above does the same operation that was earlier done by
the while loop. The for statement has three parts separated by a
semicolon:
1. int a=0; This initializes a to zero
28
2. a<101; This checks if a is less than 101 and allows the block to be
executed only if it is true
3. a++; This increments the value of a before the end braces
#include <iostream>
int main ()
{
for (int a=0; a<100; a++)
6
7
cout << a ;
if (a==50)
10
11
12
13
return 0;
14
15
}
main15.cpp: Breaking a loop
In the above code, the for loop is broken when a reaches 50 and the
loop is not more executed.
1
#include <iostream>
int main ()
int a;
cout << a;
10
11
12
return 0;
29
In the above code, when a reaches 50, due to the continue keyword,
the following lines of code in the block (cout) are not executed but
the unlike the break statement the loop is not broken, the execution
continues with the next iteration.
switch-case structure
The switch case structure is similar to a multiple if else structure.
An example is given below:
1
#include <iostream>
int main ()
int a =1;
switch (a) {
case 1:
cout << "a is 1";
break;
case 2:
10
11
12
break;
13
default:
cout << "value of a unknown";
14
15
16
return 0;
17
}
mian17.cpp: Switch Case
In the above code, the switch statement checks the value of a with
the possible cases. In case a is 1 then that segment is executed. It
might happen that a is neither 1 nor 2 in which case the default section is executed. Notice, the use of break after the end of each case
except the default case.
30
Do it yourself:
Implement the switch case code above using if else structures.
Get an integer input from the user and calculate the factorial using
loops. The code should output an error if the user enters a negative
integer.
return (x+y);
In fact the void type can be used as an input to functions but that
is an advanced concept which you can learn when you understand
pointers.
return (x+y);
32
int main ()
int a = 101;
int b = 100;
int c = add(a,b);
10
11
When we called the function add with the inputs a and b what
happens actually is that the value of a and b are copied into the
local variables x and y and the addition is done using them. The
result of the addition is then stored in c. Notice that you cannot
change the value of a or b inside the add function or for that
matter any variable declared inside the main function. Of course, one
can make these global variables so that the add function sees them
but, there are disadvantages in doing so. The way to do it in C++ is
by passing the reference instead of the value itself. Have a look the
example below:
z = x+y;
return (x+y);
int main ()
int a = 101;
int b = 100;
int c=0;
10
11
add(a,b,c);
12
13
return 0;
14
Here the add function is slightly modified-now it takes three parameters instead of two. Also notice the & sign before the third parameter.
Using this syntax (&), the value of a parameter which is not seen
by the function can be changed. Here we are storing the result of the
sum of the numbers in the third parameter c which is not actually
seen by the function. We call this method as referencing a variable in
a function. This method is also useful when you want a function to
return more than one output. For example, you want a function to
33
return both the sum and the difference, you could do this way:
sum = x+y;
diff = x-y;
int main ()
int a = 101;
int b = 100;
int s=0;
11
int d=0;
add_sub(a,b,s,d);
12
13
14
return 0;
10
15
}
main20.cpp: Passing values and references
return(x+y);
int main ()
int a = 101;
int b = 100;
return 0;
10
}
main21.cpp: Default Parameters
34
function
operation(int x=0, int y=0, int z=0, int a=0, int b=0)
operation(int x, int y=0, int z=0, int a=0, int b=0)
operation(int x, int y, int z, int a=0, int b=0)
operation(int x, int y, int z, int a, int b=0)
operation(int x=0, int y, int z, int a, int b)
operation(int x=0, int y, int z, int a, int b=0)
operation(int x=0, int y=0, int z, int a, int b)
operation(int x=0, int y=0, int z, int a, int b=0)
legal
yes
yes
yes
yes
no
no
no
no
Function overloading
Sometimes you need to write functions which have almost similar
functionality but take different input types and return different return types. You can do this using the concept of function overloading
as illustrated below:
1
return(x+y);
return(x+y);
int main ()
int a = 101;
10
int b = 100;
11
float c = 101.2;
12
float d = 102.5;
13
14
15
return 0;
16
}
main22.cpp: Function overloading
In the above example, there are two functions defined with the same
name add. These two have almost similar functionality but one
operates on integers and the other operates on float. This is an example of function overloading. You could have as well given a different
name for the function that adds floats but the benefit of using function overloading is that the user need not worry which function to
use for adding integers, the function overloading takes care of it by
checking the input parameter types and uses the appropriate function.
int age1=50;
int age2=45;
int age3=13;
int age4=15;
int age5=76;
36
int
age2 = age[2];
Do it yourself:
Try to see what happens if you try to access an element outside the
array limit, say 6 using age[6]
What is the output you get when the arrays are not initialized?
Multidimensional arrays
Multidimensional arrays are another variation of one dimensional
arrays. For example, you want to store the age of students in 2 classes
class 1 and class 2, each having 5 students then you could do it with
a two dimensional array like:
\\ class 1
\\class 2
10
11
12
13
(5*2).15
37
int c=0;
for(int ii=0;ii<size;ii++{
c += arr[ii];
return c;
10
11
int c=0;
12
for(int jj=0;jj<2;jj++{
13
for(int ii=0;ii<size;ii++{
14
c += arr[ii][jj];
15
16
17
return c;
18
}
main25.cpp: Arrays as parameters
The above example shows two functions one takes a one dimensional
array as its input. As you have noticed, the syntax is that the size
of the array is not given (i.e int arr[]). For a two dimensional array,
the size of the second dimension is given but not the size of the first
one.16
Structures
Arrays are useful for storing a collection of values which are of similar data type. Structures are a collection of a different data types. For
example you could use structures to store the age, height and weight
of a student using structures:
struct student {
int age;
float height;
float weight;
};
38
We have used multiple data types like int and float in the structure
student. The structure student is now a data type by itself and can
be used now the way int is used to denote a integer. An example
of declaring a variable of type student and initializing the values is
shown in the example below.
1
struct student {
int age;
float height;
float weight;
4
5
};
student a;
student b;
a.age=10;
10
a.height=100;
11
a.weight=30;
12
13
b.age=13;
14
a.height=127;
15
a.weight=36;
main26.cpp: Structures
struct student {
int age;
float height;
float weight;
}a,b;
Structures used in conjunction with arrays is a neat way to organize a database. For example, to have the database of a class having
100 students you just have to call an one dimensional array of structures:
1
student class1[100];
main27.cpp: Structures and Arrays
Pointers
What is a pointer?
Earlier, we have seen how to visualize the physical memory in the
computer and how a variable is stored in it.
Any variable is stored in a memory location and every memory
location has an associated address to it. A pointer is a special variable which stores the address of a memory location. A pointer to an
integer is declared like:
int *p;
There is a star after the data type int to denote that this variable is
only allowed to store address of memory locations. There can be
pointers to all the data types we have studied so far (float, double
etc.,). Pointers are not so easy for beginners to understand but once
you get a pictorial representation, it is easier to follow. Let us assume
that we are declaring a variable:
int a;
a=100;
When the first statement is executed, a memory location is allotted
but that memory location is likely to contain a random value as this
variable is not yet initialized. The pictures below show the memory
location of a before initialization and after initialization to 100.
Figure 5: The memory location of
the variable a. The variable is not yet
initialized (int a) and therefore contains
a random value 56739.
40
As shown above, the way to get the address of a is to put a & before it. Now pa contains the address of a as shown in the picture.
This is the correct way to initialize a pointer, that is only addresses
can be passed to a pointer. You cannot initialize a pointer to a random value by yourself:17
int a = 100;
int *p = 5000;
The compiler will complain if you try to do it.
41
main28.cpp: Pointers
42
Do it yourself:
In the above example, what happens when you make a statement
like *pa++? Is the pointer incremented or the value pointed by the
pointer incremented? Check it out by printing out the values of pa
and *pa after the statement
Pointer to pointers
As mentioned earlier, pointers contain the address of a variable.
In the similar way, the address of the pointer can also be stored in
another pointer. This is illustrated in the example below:
1
2
3
4
int a;
int *pa=&a; // pa contains the address of a
int **ppa=&pa;//ppa contains the address of pa
int ***pppa=&ppa;//pppa contains the address of ppa
You may add as many stars as you want but you are most likely to
use only pointers and not anything more than that and therefore we
shall not dwell much into it.
43
File I/O
In the next chapter, we will jumping into the concept of C++ but
before that I will give a short glimpse of file I/O. We will learn how
to do various operations like opening, reading, writing and closing a
file. This chapter will be brief so that you will just know how to do
simple things using files. There are other ways to manipulate files but
I will just cover only one.
The first thing to note is that we will include a header file called
fstream whenever file related operation are done18 . The general
sequence to do any file related operation is:
Open the file
Read or Write from/to the file
Close the file
File objects
Prior to doing any file operations, we should first define a file object
and that is done like:
fstream outFile;
fstream is a class in C++ which is used for basic file reading and
writing. Since we will get into classes in the next chapter, we will just
treat it like we treat int, float etc., We call outFile is an object of type
fstream.
Opening a file
Opening a file is done using the function open as shown below:
outFile.open("out.txt", ios::out|ios::app );
The first argument is the file name and the second argument is a
combination of some flags as described below:
46
flag
ios::out
ios::in
ios::app
ios::trunc
description
open file for writing
open file for reading
append file for appending
delete file (if it already exists) and open
Writing to a file
Once you have opened a file for writing, the file object can be used
the way cout was used to write on the screen as shown in the example below:
#include <iostream>
int main () {
fstream outFile;
outFile.open("test.txt",ios::out|ios::trunc);
cout<<Data written...<<endl;
10
11
#include <iostream>
#include <fstream>
int main () {
fstream inFile;
47
inFile.open("test.txt",ios::in);
int a;
inFile>>a;
cout<<a<<endl;
10
inFile>>a;
11
cout<<a<<endl;
12
inFile>>a;
13
cout<<a<<endl;
14
inFile>>a;
15
cout<<a<<endl;
16
inFile.close();
17
return 0;
18
#include <iostream>
#include <fstream>
int main () {
fstream in;
in.open("test1.txt",ios::in);
8
9
string s;
10
cout<<"Line 1:"<<endl;
11
12
cout<<s<<endl;
13
14
cout<<"Line 2:"<<endl;
15
16
cout<<s<<endl;
17
in.close();
18
return 0;
48
Closing a file
Closing a file is done using the function close() as illustrated in the
examples above. If the file is not closed, the memory might not be
freed for other uses or the file might get corrupted.
#include <iostream>
#include <fstream>
int main () {
fstream inFile;
inFile.open("test.txt",ios::in);
int a;
cout<<a<<endl;
10
11
return 0;
12
13
The function eof will return false until the end of the file is reached
and therefore the reading will continue until then.
Do it yourself:
Check what happens if you continue reading even after the end of
file in the above example.
49
As far as possible you try to avoid binary files. You will be using
ROOT files for storing most of the data in particle physics which you
will learn soon. If at all you need to read or write simple data, follow
the simple examples given in this chapter.
Do it yourself:
TIFR wants you to write a program for that stores the details like
name, age, department and salary of an employee in a database.
Implement it with the use of structures and make the program interactive. The program should asked the user to input the values and
should allow the user to decide whether to exit the program.
Classes
From C to C++
The concepts and features we have learned so far are more or less
common to C and C++. We shall now learn about classes, the main
ingredient of C++. C++ can be called as a higher version of C in that
whatever we could do with C++ can be done with C but not in a
straight forward way. Data abstraction and object oriented programming are the main features of C++. For a beginner it might seem that
the use of C++ is a bit of work around and that most of the things
could be achieved with C itself. This is true but the question here
is not the achievement but the way it is achieved and the time it
takes to achieve. It will take sometime to convince yourself about the
power of C++ and probably you might not be convinced at all until
you write a program that deserves the power of C++. You should be
aware that a language is used only to express an idea and how powerful a language is depends on how easily we can express not only
simple ideas but also abstract ideas. That is to say that just because
the expressions are easier in a particular language does not necessarily imply that the program will perform better in that language.
It depends on the algorithm and other things also. A program written in C++ can be slower than a program written Java if it is not well
written. We shall move on with these things in mind.
52
struct student {
int age;
float height;
float weight;
};
We could have easily done away with structures by just having the
age, height and weight variables as simple variables and still would
have got the program to run successfully.
int age;
float height;
float weight;
What then is the advantage of structures? The answer is that the program is more structured and organized when the details belonging
to an abstract data type are a sort of "tied together" so that the access and manipulation becomes simpler. Expressing the attributes
becomes more elegant by using structures. When you want to store
details of students of a class room without the use of structures, you
would declare some variables like
1
for the entire class which is not an elegant way.20 . So far, I have just
tried to convince you that using structures makes your program
look more concise by tying the elements of an abstract data type like
student. Let us look at the other features that structures offer.
struct Vector{
53
};
x=a;
y=b;
z=c;
8
9
10
int main () {
11
Vector v;
12
v.Initialize(1,2,3);
13
cout<<v.x<<endl;
14
cout<<v.y<<endl;
15
cout<<v.z<<endl;
return 0;
16
17
In the above example, "Initialize" is a member function of the structure Vector which sets the values of the three components of a vector.
The function is declared inside the structured but defined outside of
it. Notice the use of scope operator ::. This operator tells that this
function belongs to the structure Vector. This is needed because there
could be other structures which might have an Initializefunction.
The scope operator tells us to which structure the function belongs
to. The above example is a simple revelation of how the data abstraction, that is, to organize data types that build the abstract structure
and implement useful methods to manipulate them and to extract
useful information using the the fundamental data types in the structure, brings in simplicity into code development.
structureFunction.cpp: Function in a
structure
1
2
3
4
class Vector{
int x,y,z; // member variables
public:
void Initialize(int a, int b, int c); //member function
54
};
x=a;
y=b;
z=c;
9
10
11
int main () {
12
Vector v;
13
v.Initialize(1,2,3);
14
15
16
17
18
2
3
4
accessible by
members of the same class or friends
members of the same class, friends and derived classes
all
class Vector{
int x,y,z;
public:
void Initialize(int a, int b, int c);
55
float Length();
};
x=a;
10
y=b;
z=c;
11
12
13
float Vector::Length()
14
15
return (sqrt((x*x)+(y*y)+(z*z)));
16
17
float Vector::dotProduct(Vector a)
18
19
20
1
2
3
class Vector{
int x,y,z;
public:
56
Vector(); // ctor 2
Vector(); // ctor 2
6
7
~Vector(); // dtor
float Length();
10
};
11
12
x=a;
13
y=b;
z=c;
14
15
16
Vector::Vector(){
x=0;
17
y=0;
z=0;
18
19
Vector::~Vector()
20
21
cout<<"Deleting object..."<<endl;
22
23
float Vector::Length()
24
26
return (sqrt((x*x)+(y*y)+(z*z)));
}
27
float Vector::dotProduct(Vector a)
28
25
30
31
int main () {
29
32
33
34
35
36
37
The main difference is, unlike the Initialize function which can be
called anywhere and any number of times, the constructor is called
only when the objects are created.
Constructor overloading
A class can have many constructors each taking different arguments,
a concept similar to overloading of functions which we saw earlier.
In the above example we see the explicit declaration of two construc-
57
tors one that takes three parameters and another one that take no
parameters. To create an object of this class you can use either one of
these but notice the way how the object is created using the first constructor and the second constructor. There is no bracket used when
created using the second constructor though that constructor definition has them. This is an important point to remember that if the
constructor has no parameters you need not use the brackets when
you create object.
Default Constructor
What happens if the constructor for a class is not explicitly declared?.
The compiler takes over and writes a dummy constructor- one that
does nothing. This is called as the default constructor. A default
constructor will be made only if there are no existing constructors
defined.
Destructor
A destructor is the opposite of the constructor. It is called when an
object is deleted. The syntax for defining a constructor is a (tilde) followed by the class name as shown in the above example. As you will
notice, the destructor is called before the end of the program automatically even without an explicit call to it22 . Destructor overloading
is not allowed in C++.
Pointers to classes
Just like we had pointers to other data types, we can also have pointers to classes. Before that let us see how a pointer to a structure is
created and how we can access the variables of a structure using
pointers.
struct Vector{
int x,y,z;
};
5
6
x=a;
y=b;
z=c;
9
10
11
int main () {
58
Vector v;
12
14
15
cout<<"Values of x,y,z"<<endl;
16
cout<<v.x<<endl;
17
cout<<v.y<<endl;
18
cout<<v.z<<endl;
19
return 0;
13
20
As demonstrated in the example above, we create a pointer to a structure pv as usual using the *. This pointer is initialised with the
address of v using the address operator &. We call v as a object
and pv as the pointer to the object. To access a member function
using the object we use the . (dot) operator. For example to call
the initialize function using the object, we would have called
v.Initialize(1,2,3). In the above example we call this function using the pointer instead. Notice the arrow operator in place of the
dot. This is the operator to be used to access a member using a
pointer.
The same procedure applies for classes as well. However creation
of pointer is made in a different way for classes as shown below.
structurePointer.cpp: Pointer to a
structure
1
2
class Vector{
int x,y,z;
59
public:
Vector(); // ctor 2
~Vector(); // dtor 1
float Length();
9
10
};
Vector::Vector(int a, int b, int c){
11
x=a;
12
y=b;
z=c;
13
14
15
Vector::Vector(){
x=0;
16
y=0;
z=0;
17
18
Vector::~Vector()
19
{
cout<<"Deleting object..."<<endl;
20
21
22
float Vector::Length()
23
{
return (sqrt((x*x)+(y*y)+(z*z)));
24
25
26
27
28
float Vector::dotProduct(Vector a)
29
{
return(x*a.x + y*a.y + z*a.z);
30
31
32
int main () {
33
34
35
36
37
// using pointers
Vector *v3 = new Vector(1,2,3); // using ctor 1
Vector *v4 = new Vector();// using ctor 2
38
39
40
41
42
43
44
45
}
classPointer.cpp: Pointer to a class
60
Operator Overloading
Suppose you make a class for a 2 x 2 matrix, you will include all
relevant methods in the class like the determinant. Now, if you want
to add two matrices, you cannot just use the +operator and get the
answer. The + operator works directly for integers, floats etc., but
when using with classes, it is not so because it is not directly relevant
which is to be added to what. However, you make it relevant by the
mechanism of function overloading.
1
class twoBytwoMatrix{
public:
float a11,a12,a21,a22;
twoBytwoMatrix(); // ctor 2
~twoBytwoMatrix(); // dtor 1
float determinant();
10
};
11
12
13
14
twoBytwoMatrix out;
15
16
17
18
19
20
21
22
23
twoBytwoMatrix out;
61
24
25
26
27
28
29
30
31
32
a11=a;
33
a12=b;
34
a21=c;
35
a22=d;
36
37
twoBytwoMatrix::twoBytwoMatrix()
38
{
a11=0;
39
a12=0;
a21=0; a22=0;
40
41
twoBytwoMatrix::~twoBytwoMatrix()
42
{
// cout<<"Deleting object..."<<endl;
43
44
45
float twoBytwoMatrix::determinant()
46
{
return ((a11*a22)-(a21*a12));
47
48
49
int main () {
50
twoBytwoMatrix m1(1,2,3,4);
51
twoBytwoMatrix m2(2,3,4,5);
52
twoBytwoMatrix m3;
53
m3=m1+m2;
54
cout<<"Determinant: "<<m3.determinant()<<endl;
55
return 0;
56
In the above example, we have overloaded the addition and subtraction operators. The syntax is as follows:
return type operator + (input parameter type)
The + operator can be replaced by most of the operators we have
learnt so far like -,*,/ etc.,. When the operator is a binary operator
(an operator which has operands before and after it) the left hand
operand is an object of the class. The type of the right hand operand
is given in the input parameter type and the output type is given in
the return type.
operatorOverloading.cpp: Overloading
operators
62
In the above example we saw that the + operator has been overloaded to perform the addition of two matrices. However, note that
the same thing can be done by replacing it by the - operator. That
is you can overload the subtraction operator to perform an addition.
This is completely legal and valid but such things are never done.
It is always better to overload the operators to perform what their
sign signifies rather than confusing yourself and other end users
eventually.
Static members
In the above example, we created three matrix objects m1, m2 and
m3. When each of these objects are created, a memory location is
allocated for their members. Now m1 has its set of member data and
functions (a11, a12 ... etc.,) and so do m2 and m3. There might be
cases where you want to share a variable across all the objects of a
class. That is, a variable that has an unique value for all the objects.
Such variables are called static variables and they are somewhat like
global variables to a class. The way they are declared and initialized
is different from the way it is done for other variables. Lets see an
example using static variables to keep track of the number of objects
created.
1
2
class twoBytwoMatrix{
public:
float a11,a12,a21,a22;
7
8
};
twoBytwoMatrix::twoBytwoMatrix(float a, float b,
float c, float d){
10
11
a11=a;
12
a12=b;
13
a21=c;
14
a22=d;
15
N++; // incrementing N
16
17
18
19
int main () {
20
21
twoBytwoMatrix m1(1,2,3,4);
63
22
23
twoBytwoMatrix m2(2,3,4,5);
24
25
26
return 0;
27
28
29
twoBytwoMatrix::twoBytwoMatrix(float a, float b,
float c, float d){
2
3
a11=a;
a12=b;
a21=c;
a22=d;
7
8
int main () {
10
11
twoBytwoMatrix m1(1,2,3,4);
12
13
return 0;
14
}
this.cpp: The this keyword
Friendship
In the last class we saw the use of access specifiers: public, private
and protected. The private and protected members can be accessed
by friend functions and friend classes. Let us see the use of friend
function using examples:
class rect{
float length, breadth;// private mem
2
3
public:
rect(float a, float b){
4
5
length = a;
breadth = b;
float GetArea();
10
};
11
float rect::GetArea()
12
{
return length*breadth;
13
14
66
15
16
float GetLength(rect r)
17
{
return r.length;
18
19
20
int main() {
21
rect first(2,3);
22
23
24
return 0;
25
}
friendFunction.cpp: Friend function
In the above example, we have made a class for the shape called
rectangle which has its length and breadth as its private members. It
also has a public method to compute the area of the rectangle. Now
the user has implemented a function called Getlength() which returns
the length of the rectangle. Since length is a private member it is not
accessible by ordinary functions. However, if the function is declared
as a friend inside the class, then it can access the private members
also. The syntax to declare a function as a friend is as shown in the
example above:
friend float GetLength(rect r);
Like friend functions, there can be friend classes too. If a class is
declared as a friend, the friend class can access the members of the
class that declared as a friend (not vice-versa):
1
class square{
float length;// private
2
3
public:
square(float a){//ctor
length = a;
5
6
9
10
11
12
13
};
class rect{
float length, breadth;// private
public:
rect(float a, float b){//ctor
14
length = a;
15
breadth =b;
16
17
float GetArea();
18
float GetArea(square);
67
19
};
20
float rect::GetArea()
21
{
return length*breadth;
22
23
24
float rect::GetArea(square s)
25
26
length=s.length;
27
breadth=s.length;
28
return GetArea();
29
30
int main() {
31
square s(2);
32
rect r(3,4);
33
34
35
}
friendClass.cpp: Friend class
Inheritance
Now we come to an important concept in object oriented programming called inheritance. Inheritance is a method by which classes
which share some of the features with other classes can inherit methods and variables common to both of them thus reducing the code
size.
1
class shape{
protected:
3
4
5
length=0;
breadth =0;
68
10
11
12
length=l;
13
breadth =b;
}
14
15
};
16
17
public:
square(float a){//ctor
18
19
20
length = a;
21
breadth=a;
22
23
float area(){
return
24
length*breadth;
25
26
};
27
28
public:
rect(float a, float b):shape(a,b){//ctor
29
30
31
32
float area(){
return length*breadth;
33
34
35
};
36
int main() {
37
square s(2);
38
rect r(3,4);
39
40
41
}
inherit.cpp: Inheritance in classes
In the above example, there is a class called shape which has two
variables length and breadth. The shape and rect classes too have
these variables and therefore can make use of the shape class using
the feature of inheritance. The syntax is as shown in the example:
class rect:public shape
class square:public shape
The first class in the above syntax is called as the derived class
and the second class, the base class. The derived class can access the
members of the base class as if they if their own member variables.
The access specifier public can be replaced by other access specifiers
like protected and private.23 Note that the square and rect classes
69
Multiple inheritance
Classes can inherit multiple classes too. In the example below there
is a simple class for printing out the values of an input float variable.
The square inherits from the shape and the Print class. As with single
inheritance, the square class can access the members of the Print class
as if it were is its own member.
1
class Print{
public:
Print(){
cout<<"Printing Value:"<<a<<endl;
};
class shape{
10
11
12
13
protected:
float length, breadth;
public:
shape(){
14
15
length=0;
16
breadth =0;
17
18
19
20
length=l;
21
breadth =b;
70
22
23
};
24
25
public:
square(float a){//ctor
26
27
28
length = a;
29
breadth=a;
30
31
float area(){
return
32
length*breadth;
33
34
};
35
int main() {
36
square s(2);
37
s.PrintValue(s.area());
38
}
multInherit.cpp: Inheritance in classes
Polymorphism
The next feature in C++ is polymorphism. We have seen what a base
class is and what a derived class in previous examples. The shape
class was a base class and square was a class derived from shape.
Let us create a pointer for the derived class square: square *s=new
square(2);
Now let us create a pointer for the base class shape: shape *sh=new
shape();
Let us proceed to the next logical step. Now since square is a
derived class from shape, its pointer is type compatible with shape
and therefore we can as well do:
shape *sh=s;
This type compatibility of the pointer of the base class with the
derived class is called polymorphism. That is the pointer of the base
class can point to the pointer of the derived class. Note however the
vice-versa is not true:
square *s=sh;
The above (reverse polymorphism) is not allowed in C++. The
following is an example of polymorphism:
class shape{
protected:
71
public:
shape(){
5
6
length=0;
breadth =0;
10
11
12
length=l;
13
breadth =b;
14
15
void PrintValues(){
cout<<"Length: "<<length<<" Breadth: "<<breadth<<endl;
16
17
18
};
19
20
public:
square(float a){//ctor
21
22
23
length = a;
breadth=a;
24
25
26
float area(){
return
27
length*breadth;
28
29
};
30
int main() {
square *s=new square(2);
shape *sh1= new shape();
31
32
33
34
35
sh1->PrintValues();
36
sh2->PrintValues();
37
38
}
poly.cpp: Polymorphism in classes
Virtual Members
In the above example, thougth there was type compatibility of the
base class with the derived class, there was one pitfall in it. The base
class pointer thus obtained cannot still access the members of the
derived class. square *s=new square(2);
shape *sh2= s;
72
In the above statements, the base class pointer sh2 points to the
derived class pointer s. Though the pointer s can access its member
variable area directly:
Allowed
s->area();
the pointer sh2 however cannot access it:
Not allowed
sh2->area();
There is a workaround in such cases: move the area method into
the base class. But remember that we will lose the flexibility in doing
so for if we implement a another class called triangle where the area
is calculated differently, this we lead to a complex code structure.
This is where one can use virtual members. If a particular method
is common to all the derived classes but have different implementations, a virtual method should be defined in the base class as shown
in the example below:
class shape{
protected:
float length, breadth;
4
5
public:
shape(){
6
7
length=0;
breadth =0;
10
11
12
13
length=l;
breadth =b;
14
15
16
void PrintValues(){
cout<<"Length: "<<length<<" Breadth: "<<breadth<<endl;
17
18
19
20
return -1;
}
21
22
};
23
24
public:
25
square(float a){//ctor
26
27
length = a;
73
breadth=a;
28
29
30
float area(){
return
31
length*breadth;
32
33
};
34
35
public:
tri(float a, float b){//ctor
36
37
38
length =
39
breadth=
a;
b;
40
41
float area(){
return
42
0.5*(length*breadth);
43
44
};
45
int main() {
square *s=new square(2);
tri *t=new tri(2,3);
46
47
48
49
50
51
52
53
}
virtual.cpp: Virtual methods in classes
In the above code, the two classes square and tri have a common
method called area which are implemented differently. For the base
class to access these members, a virtual method for area has been
defined in the base class.
{ return (0); }
74
Templates
Function templates are special functions than can adapt themselves
to different data types.
1
T Subtract (T a, T b) {
T diff= a-b;
return diff;
U Subtract2 (T a, U b) {
U diff= a-b;
return diff;
9
10
11
12
int main () {
13
float a,b;
14
int c,d;
15
a=2.2;
16
b=5.6;
17
c=3;
18
d=4;
19
float d1=Subtract<float>(a,b);
20
21
float d3=Subtract2<int,float>(c,a);
22
23
24
25
26
In the above code we have declared a template function called Subtract which takes in two variables of type T and returns the same
type T, the difference between the inputs. T is just a place holder
and any other letter can be used in its place. The statement: template
<class T>
tells that the function following takes a template variable T. The
Subtract function can take any of the data types now by replacing the
template variable: float d1=Subtract<float>(a,b);
takes float as its inputs and
int d2= Subtract<int>(c,d);
takes int as its inputs. A template function can takes different
types as its input also by declaring a statement like:
75
class square{
T side;
public:
square(T a){//ctor
side=a;
6
7
T area(){
return a*a;
10
11
};
12
int main () {
13
square<int> s1(2);
14
square<float> s2(3.3);
15
cout<<"area of s1 "<<s1.area()<<endl;
16
cout<<"area of s2 "<<s2.area()<<endl;
return 0;
17
18
}
classTemplate.cpp: Class Template
Installing ROOT
The installation of ROOT (in Linux) consists of three steps which will
be explained briefly in the following subsections. The sections will
not describe advanced features as it is mainly aimed at beginners
trying to learn ROOT. If you need more details about it you can refer
to the ROOT website.
Downloading ROOT
The source code of ROOT is available for free download at:
http://root.cern.ch/drupal/content/downloading-root
ROOT regularly releases new updates and patches for bugs found
in their released source code. In the link above, you will find a huge
list of ROOT versions available for download. It is usually recommended to use the latest version to make sure that you up to date
with the features. However, it might be the case that the codes written for your experiment depend on a specific version of ROOT in
78
Compiling ROOT
Once you have downloaded the ROOT source tree, follow the steps
here:
1. Un zip/tar in a convenient location and the contents will be stored
in a folder called root, by default.
2. cd to the root folder
3. type ./configure
4. make -j 4
As described in Chapter 2, one can create objects of functions (with
declarations in the header file and definitions in the source file) so
that another program can easily link to these objects without having to re-compile the sources and headers26 . The rules for compiling
ROOT will depend on the Operating System and the libraries that
are installed in your system. These rules are generated automatically by the ./configure command which will generate a makefile.
The makefile contains the commands for compiling the sources and
headers for your system. The number 4 in the make command is the
number of cores that will be used for the compiling process. You can
change it to the number of cores in your machine. Once you type the
make command, the compilation process will start and you see lines
of compile commands starting with g++. This is similar to what you
did for compiling the codes in the examples. ROOT contains thousands of files and each has to compiled and therefore it takes sometime to complete. Sometimes, the compilation process might stop
with an error. In that case, it might be that you have not installed
some libraries on which the compilation of ROOT depends. Check:
what the error is from the compilation output and try to check where
the error first started by scrolling up. That line will show you some
information like for example -lGl not found in which case you
should install the GL library. Once you have installed it, you can
restart the compilation by typing the make -j 4 command. The
compilation will restart from the place where it last stopped.
79
80
yourself in the terminal using the previous method. Now you are all
set to run ROOT. In the terminal, type root and you will enter into
the ROOT terminal. Type: TCanvas t and press Enter and you will
see a canvas popping up. Enter .q in the terminal and you will be out
of the ROOT terminal.
#include <iostream.h>
int rootTest()
return 0;
Let us see how the code above can be run using the ROOT CINT
interface. In a terminal, cd to the location where the above file is
present and start the ROOT CINT interface by typing root and then
type:
.x rootTest.C
The program will be executed and you should see the output. You
can exit the root terminal by typing:
.q
You can use any of the ROOT classes in these files which will be
executed by the ROOT CINT interface. More ways to ron a ROOT
program can be found in this wiki link.
#include <iostream>
#include <TApplication.h>
#include <TCanvas.h>
using namespace::std;
int main()
{
TApplication *t = new TApplication("myapp",0,0);
TCanvas *C = new TCanvas;
7
8
9
10
t->Run();
11
delete C;
return 0;
12
13
If you try to compile the above program with the usual compile
command:
g++ canvas.cpp
The compiler will complain that it could not find the header
files (TApplication, TCanvas etc.,). These file reside in the include
folder present in the location where ROOT is installed ($ROOTSYS/include). You will have to inform the compiler about this location so that it can search for these file there. The flag used for the
search directory for include files is -I. The compile command now
looks like:
g++ canvas.cpp -I$ROOTSYS/include
Executing the above command gives errors like undefined reference to TApplication::TApplication etc.,. This is because though
the header files where the functions were declared are given, the pro-
82
gram cannot find the definitions for those functions. We have already
compiled ROOT and have made the objects files for all the files so we
need to link to those object files (or libraries) using the flag -l. The
flag used for the search directory for library files is -L. The compile
command looks like:
g++ rootTest.C -I$ROOTSYS/include -L$ROOTSYS/lib -lCore
-lGpad
where $ROOTSYS/lib denotes the location where the ROOT libraries are located and libCore and libGpad are the libraries where
the definitions for the TApplication and TCanvas classes are found.If
you use other classes, you should link to the appropriate libaries
Now you will see the program compiles successfully and you can run
it using:
./a.out
Getting the correct compiler flags for program using ROOT libraries
As you saw earlier, you need to know the correct flags for the include
directory and for linking to the correct libraries. There is easier and
faster way of doing this. ROOT provides a program called rootconfig to output these flags for your system. This program is found
in the folder:
$ROOTSYS/bin
If you just execute root-config on a terminal, it will shown the
options for using the program. FOr example using the option
glibs will give you the libraries required for linking and cflags
will give you other options requires for compiling like the include
folder path etc.,.Type:
root-config glibs
and you will see an output containing the libraries to be linked to
for a root program:
-L/home/myprogram/root/lib -lGui -lCore -lCint -lRIO -lNet
-lHist -lGraf -lGraf3d -lGpad -lTree -lRint -lPostscript -lMatrix lPhysics -lMathCore -lThread -pthread -lm -ldl -rdynamic
You can use execute the root-config program directly in your compile command and automatically append these flags in this manner:
g++ canvas.cpp root-config --glibs --cflags
Plotting a histogram
Let us move to a real world example where we will plot a distribution of a variable using a histogram. Let us see how to generate a
gaussian distribution and poisson distribution of random number
and plot it in a canvas using ROOT.
83
#include <iostream>
#include <TApplication.h>
#include <TCanvas.h>
#include <TH1F.h>
#include <TRandom.h>
int main()
TApplication app("my_app",0,0);
10
11
12
14
15
h2->SetFillColor(6);
16
for(int ii=0;ii<10000;ii++){
17
h1->Fill(ran->Gaus(0,100));
18
h2->Fill(ran->Poisson(40));
19
20
c->Divide(2,1);
21
c->cd(1);
22
h1->Draw();
23
c->cd(2);
24
h2->Draw();
25
gPad->Update();
26
app.Run();
27
delete ran;
28
delete c;
29
return 0;
30
13
For the above job, we will use the following three classes in ROOT:
1. TApplication: creates the ROOT application environment
2. TCanvas: Opens a canvas
3. TRandom: Generates random numbers
4. TH1F: Histogram class
In the above code, we create an object of TApplication which should
be done only once in your program. Without the TApplication class,
your canvases will not be shown. The Run method should be called
84
Saving a canvas
Once you have plotted anything on a canvas, you can save them in a
familiar format like pdf, png, jpg etc.,.You can use the Print function
in the TCanvas class like this:
c->Print("canvas.pdf");
85
to generate a pdf file. The canvas file menu also has a Print or Save
option which also helps you to do it.
int main()
//App instance
TApplication app("my_app",0,0);
// create histograms
9
10
11
13
14
h1->SetFillColor(4);
15
h2->SetFillColor(6);
16
h3->SetFillColor(5);
17
h4->SetFillColor(7);
18
12
19
20
88
21
h1->Fill(ran->Gaus(0,100));
22
h2->Fill(ran->Poisson(40));
23
h3->Fill(ran->Gaus(0,100),2);// weighted by 2
24
h4->Fill(ran->Poisson(40),2);// weighted by 2
25
26
// draw histograms
27
c->cd(1);
28
h1->Draw();
29
c->cd(2);
30
h2->Draw();
31
c->cd(3);
32
h3->Draw();
33
c->cd(4);
34
h4->Draw();
35
36
h1->GetXaxis()->SetTitle("Gauss Values");
37
h1->GetYaxis()->SetTitle("Counts");
38
gPad->Update();
39
app.Run();
40
delete ran;
41
delete c;
42
return 0;
The above example illustrates the use of weights. As you can see, the
second argument of the Fill function gives the weight. By default, it is
set to one. Histograms h1 and h2 are filled with no weights and in h3
and h4 all values are filled with a weight of 2. The number of counts
(not the entries, see the y axis) in the histograms h3 and h4 (Refer
Fig.10) are therefore double the counts in the histograms h1 and h2.
Adding histograms
Just like you add ordinary numbers, you can also add histograms
too. In histograms, the number of counts in a particular bin in the
first histogram is added to the number of counts in the same bin in
the second histogram. One has to be careful here while setting the
bin size and the number of bins in the histograms. They should be
similar for both the histograms or you will end up getting wrong
results.
1
int main()
// app instance
89
TApplication app("my_app",0,0);
//canvas creation
// histogram creation
9
10
11
12
// styles
13
h1->SetFillColor(4);
14
h2->SetFillColor(6);
15
h3->SetFillColor(5);
16
17
18
19
h1->Fill(ran->Gaus(0,100));
20
h2->Fill(ran->Gaus(0,100));
21
22
c->cd(1);
23
h1->Draw();
24
c->cd(2);
25
h2->Draw();
26
// add histograms
27
h3->Add(h1,h2);
28
c->cd(3);
90
29
h3->Draw();
30
gPad->Update();
31
app.Run();
32
delete ran;
33
delete c;
34
return 0;
35
}
histo3.cpp: Adding two histograms.
Figure 11: Adding two histograms
In figure 11, the top one is the first histogram and the middle
one is the second histogram, having the same bin size and range as
the first one. The bottom one is the result of these two histograms.
Notice that the counts in the third for each bin is equal to the counts
in the first plus the counts in the second.
// styles
h1->SetFillColor(4);
91
h2->SetFillColor(6);
7
8
h1->Fill(ran->Gaus(0,50));
h2->Fill(ran->Gaus(0,100));
10
11
12
//draw
13
h1->Draw();
14
15
16
first
92
h1->SetFillColor(4);
h2->SetFillColor(6);
7
8
h1->Fill(ran->Gaus(0,50));
h2->Fill(ran->Gaus(0,100));
10
11
12
13
14
hs.Add(h1);
15
hs.Add(h2);
hs.Draw(); //try "nostack" option
16
17
For using the THStack class, you should have your histograms created and call the Add function giving the pointers as the argument as
shown in the above example.
1
2
3
4
h1->Fill(ran->Gaus(0,50),ran->Gaus(0,50));
5
6
// draw histogram
h1->Draw("SURF2");
The Draw function takes the drawing options which are listed in the
ROOT documentation.
93
Profile histograms
Profile histograms are a replacement for 2d histograms and are plotted using the TProfile class. The x axis contains the x bins and the y
axis shows, the mean of y and the corresponding error as against the
y bins in a 2d histogram.
TProfile *hprof = new TProfile("hprof","Profile of pz versus px",100,-4,4,0,20);
// fill histograms with random numbers
1
2
for(int ii=0;ii<50000;ii++){
ran->Rannor(px,py);
pz = px*px + py*py;
hprof->Fill(px,pz,1);
8
9
10
11
// draw histogram
12
hprof->Draw();
13
}
histo7.cpp: Plotting a profile histogram.
94
Fitting a histogram
The next part of histogram is the fitting. The fit panel can be accessed
by right clicking on the histogram data points in the canvas. To do it
in C++, you should first create the function to fit using the TF1 class.
There are some pre-defined functions like gauss, pol0, pol1, pol2,
expo, landau etc., which do not require any initialization. However
if you user your own function for fitting, you should initialize the
parameters.
1
2
h1->SetFillColor(4);
5
6
7
8
//draw
first
10
h1->Draw();
11
12
gStyle->SetOptFit(1011);
13
14
//h1->Fit("gaus");
15
16
95
17
// parameter names
18
func->SetParNames("Factor","Mean","Sigma");
19
//Set Parameters:
20
func->SetParameter(0,1);
21
func->SetParameter(1,0);
22
func->SetParameter(2,50);
23
24
25
26
27
28
29
30
}
histo9.cpp: Fitting a histogram.
In the above example, the user has defined his own gaussian function
using the TF1 class and initializes the parameters using the SetParameters function. After this, the Fit function of the histogram class
is called with the TF1 pointer as its argument. The option R can be
used to restrict the fitting range to the one set in the TF1 pointer. The
fitting results can be accessed by the GetParameter function.
Figure 15: Fitting a histogram.
Graphs
Graphs are plotted using the TGraph class. Unlike histograms, there
is no binning here. There are manys to fill a graph one of them is
96
to fill the x and y arrays and give them as a input to the TGraph
constructor.
1
int n =5000;
4
5
x[ii]= ran->Gaus(0,50);
y[ii]=x[ii]*x[ii];
9
10
11
12
13
14
Like the histogram classes, the TGraph class also takes drawing options.
Figure 16: Plotting a graph.
97
first graph that is drawn and the SAME option should be added
for the following graphs (lsame,*same etc.,). The a option
should be used only for the first graph.
Similar to the THStack, there is the TMultiGraph class which takes
care of all the axis management for plotting multiple graphs.
1
int n =5000;
5
6
x[ii]= ran->Gaus(0,50);
y[ii]=x[ii]*x[ii];
x1[ii]= ran->Gaus(0,150);
8
9
y1[ii]=x1[ii];
10
11
TGraph *graph1
TGraph *graph2
12
13
= new TGraph(n,x,y);
= new TGraph(n,x1,y1);
14
graph1->SetLineColor(kBlue);
15
graph2->SetLineColor(kRed);
16
18
19
m->Add(graph2,"lp");
20
m->Draw("a");
17
The graphs are added to the TMultigraph object with their appropriate drawing options, however without the a option. The a option
has to be given only while drawing the multigraph.
Plotting a 2D graph
If you want to plot a surface or a 2d graph, then the TGraph2D class
in used. The filling can be done using an array like the TGraph class
or by using the SetPoint function:
1
2
3
4
5
6
7
//
98
dt->SetPoint(ii,x,y,z);
8
9
10
11
12
//
gStyle->SetPalette(1);
dt->Draw("surf1");
histo10.cpp:Plotting a 2D graph.
Figure 17: Plotting a 2D graph using
the TGraph2D class.
Fitting a graph
Fitting can be done following the same procedure illustrated for
histograms.
h1->SetFillColor(4);
h2->SetFillColor(6);
10
11
12
// fill histograms
TRandom *ran = new TRandom(450);
for(int ii=0;ii<10000;ii++){
13
h1->Fill(ran->Gaus(0,50));
14
h2->Fill(ran->Gaus(0,100));
15
16
h1->Draw();
17
h2->Draw("SAME");
18
gPad->Update();
19
20
cin>>a;
21
// close file
22
f->Write();
23
f->Close();
100
which describes the access options and the third parameter is the title
of the file. The access option can be one of the following:
NEW or CREATE: create a new file and open for writing. If the file
already exists it is not opened.
RECREATE: Same as NEW or CREATE but if the file already exists
it is overwritten.
UPDATE: Open an existing file for writing. If no file exists, it is
created.
READ: Open file for reading (default).
Other options exist and can be seen in the ROOT documentation.
As shown in the example, after the file is created, the histogram
pointers are created and therefore these histograms belong to that
file. The histograms are filled with values. The objects are written to
the file using the Write() function and then closed using the Close()
function. Let us see how to read the contents of a TFile.
1
2
3
4
5
h1->Draw();
gPad->Update();
cin>>a;
10
// close file
11
f->Close();
To read, you should open the file using the READ option. You
should also know the object name and the object type that you want
to read from. In the above example, we are reading from the file
created using the last file write example. The Get function in the
TFile takes the object name as the argument and then returns a TObject pointer. This TObject pointer should then be type-casted to the
type of the object you are reading like in the example above. The file
should be closed at the end of all operations.
101
1
2
// create histograms
//f1->cd();
5
6
7
// set style
h1->SetFillColor(4);
10
h2->SetFillColor(6);
11
// fill histograms
12
13
14
h1->Fill(ran->Gaus(0,50));
15
h2->Fill(ran->Gaus(0,100));
16
17
h1->Draw();
18
h2->Draw("SAME");
19
gPad->Update();
20
21
cin>>a;
22
// close file
23
f1->Write();
24
f2->Write();
25
f1->Close();
26
f2->Close();
In the above example we have created two files f1 and f2. Let us see
what happens for the following cases in the above example:
1. f1->cd() and f2->cd() both commented: The histograms are written
in f2 and nothing is written in f1. This is because the file that is
created last will be the active one and all the objects will be written
into it. (Just like a directory).
2. f1->cd() is used and f2->cd() is commented: The histograms will
be written in f1 and nothing in f2. The command cd changes the
active file to f1.
3. f1->cd() is commented f2->cd() is used: The histograms will be
written to f2 and nothing in f1.
4. Both f1->cd() and f2->cd() are used: The first histogram will be
saved in f1 since f1->cd() is called before its creation and then
since f2->cd() is called, the second histogram is saved in f2.
102
float E,P;
TFile *f1 = new TFile("treefile.root","RECREATE","My root file1");
TTree *tr = new TTree("Tree","My Tree");
tr->Branch("Energy",&E,"E/F");
tr->Branch("Momentum",&P,"P/F");
6
7
P = E*E;
tr->Fill();
9
10
11
12
tr->Draw("E");
13
//tr->Draw("E","E<10");
14
//tr->Draw("E:P");
15
gPad->Update();
104
16
17
cin>>a;
18
f1->Write();
19
f1->Close();
The picture you should have while using a TTree is that of a spreadsheet. In a spreadsheet we have columns, which in TTree is called
as a branch, the rows are called as entries. Let us fill our TTree with
two parameters E and P and try plot it using the TTree Draw function. We will store our TTree in a TFile to view it offline. The TTree
is created using its constructor which takes the name and the title
of the tree as arguments. The tree name can be used to retrieve the
tree from the file. To create branches, we use the Branch function.
This function takes the branch name, the address of the variable from
where the data to be written will be taken and the variable name and
its type in a special format. We will simply use the variable name E
for E and P for P. Since both are floats, the syntax is:
E/F and P/F
The other variable types and the syntax to be used are as follows:
C : a character string
B : an 8 bit signed integer
b : an 8 bit unsigned integer
S : a 16 bit signed integer
s : a 16 bit unsigned integer
I : a 32 bit signed integer
i : a 32 bit unsigned integer
F : a 32 bit floating point
D : a 64 bit floating point
L : a 64 bit signed integer
l : a 64 bit unsigned integer
O : [the letter o, not a zero] a boolean
Once the branches are created, the next step is to fill our data
using the Fill command. When this command is called, the data that
is stored in E and P (the addressess of which were used to create the
branches), at that point of time are written as an entry in the tree.
Therefore the number of entries stored in the tree will be the number
of times the Fill command was called.
Now once the data is stored we can use the tree object to plot them
directly. For example, to plot the histogram of E you can call:
tr->Draw(E);
Note here E is the variable name that we gave for the parameter
E. If you had given the variable name for E as energy then you can
call plot using:
tr->Draw(energy);
105
1
2
//tr->Draw("E","E<10");
//tr->Draw("E:P");
gPad->Update();
3
4
10
cin>>a;
11
f1->Close();
To view the contents of a tree stored in a TFile, you can follow the
method mentioned in the last chapter to view the contents of TFile.
1
2
int LayerNumber;
int StripNumber;
float noiseRate;
} RPCNOISERATE;
5
6
int main()
106
// app instance
TApplication app("my_app",0,0);
10
RPCNOISERATE nr;
11
int a;
12
// create canvas*
13
14
15
16
17
18
19
20
// create file
TFile *f1 = new TFile("treefile.root","RECREATE","My root file1");
TTree *tr = new TTree("Tree","My Tree");
tr->Branch("RPCNR",&nr,"L/I:S/I:N/F");
TRandom *ran = new TRandom(450);
for(int ii=0;ii<12;ii++){
for(int jj=0;jj<32;jj++){
21
22
nr.LayerNumber = ii;
23
nr.StripNumber = jj;
24
nr.noiseRate = ran->Gaus(0,50);
25
tr->Fill();
}
26
27
28
tr->Draw("L:N");
29
//tr->Draw("E","E<10");
30
//tr->Draw("E:P");
31
gPad->Update();
32
33
cin>>a;
34
f1->Write();
35
f1->Close();
In the above example, we have created a structure called RPCNOISERATE which contains the variables LayerNumber, StripNumber and
the noiseRate. To create a branch this structure we call the Branch
function like this:
1
tr->Branch("RPCNR",&nr,"L/I:S/I:N/F");
107
is appended since its a float. All these string are concatenated with a
:. This is the syntax to be followed if a branch has to be made like a
structure. The filling part is similar to the previous example.
float E,P;
// create file
TFile *f1 = new TFile("treefile.root","READ","My root file1");
TTree *tr = (TTree*)f1->Get("Tree");
tr->SetBranchAddress("Energy",&E);
for(int ii=0;ii<tr->GetEntries();ii++){
tr->GetEntry(ii);
cout<<E<<endl;
In the above example, we read a tree named Tree from a file (the file
we created in the first example in this chapter) and we know that
it has a branch named Energy. To read the value of it, you have to
supply the address where the value will be read into. This is done
using the SetBranchAddress function wherein we supply the branch
name and the address. We now loop over all the entries and use the
GetEntry function which takes the entry number as the argument
and stores the value of that entry in the address given in the SetBranchAddress (i.e in E). The GetEntry function does the opposite of
Fill.