C++ Container classes

In ROOT and C++, there are three general categories of containers you have to know about.

Arrays

Do a web search on “C++ arrays” to learn about these containers.1 Briefly, to create a double-precision array of eight elements, you could say:

Double_t myArray[8];

To refer to the 3rd element in the array, you might use (remember, in C++ the first element has an index of 0):

Int_t i = 2;
myArray[i] = 0.05;

If you’re new to C++, it won’t be obvious that while myArray[2] is a Double_t object, the type of the name myArray (without any index) is Double_t*, or a pointer to a Double_t.

Getting confused? Let’s keep it simple. If you’ve created arrays with values and errors…

Double_t xValue[22];
Double_t xError[22];
Double_t yValue[22];
Double_t yError[22];

…and you’ve put numbers into those arrays, then you can create a TGraphErrors with:

TGraphErrors* myPlot = new
TGraphErrors(22,xValue,yValue,xError,yError);

Note

Did you notice a problem with that example? I had to supply a fixed value for the number of points in each array to make the plot. In general, you won’t be able to do that; in fact, in subsequent exercises you can’t do that.

In C++, one way to get around this problem is to use “dynamic arrays.” I’ll let you read about those on the web (search on “C++ dynamic arrays”), but I’m not going to say more about them, because I rarely use them.

ROOT’s containers

ROOT’s container classes are described in chapter 16 of the ROOT Users Guide.

Note

In the TGraphErrrors constructors, the TVectorF and TVectorD classes are containers for single- and double-precision real numbers respectively. Click on the class names in the ROOT web site to see the clear and detailed explanation of how to use them.2

I’ll be blunt here, and perhaps editorialize too much: I don’t like ROOT’s collection classes. The main reason is that most of them can only hold pointers to classes that inherit from TObject. For example, if you wanted to create a TList that held strings or double-precision numbers (TString and Double_t in ROOT), you can’t do it.3

You need to know a little about ROOT’s collection classes to be able to understand how ROOT works with collections of objects. For any other work, I’m going to suggest something else:

C++ Standard Template Library (STL)

Do a web search on “standard template library”. Skim a few sites, especially those that contain the words “introduction” or “tutorial”. You don’t have to get too in-depth; for example, you probably don’t have enough time today to fully understand the concept of iterators.

Note

Did you guess that STL is my preferred method of using containers in C++?

The Standard Template Library is an important development in the C++ programming language. It ties into the concepts of design patterns and generic programming, and you can spend a lifetime learning about them.4

Vectors

Note

For the work that you’ll be asked to do in the Advanced Exercises, Expert Exercises, and probably for the rest of this summer, there’s only one STL class you’ll have to understand: vectors. Here are the basics.

If you want to use vectors in a program, or even a ROOT macro, you have to put the following near the top of your C++ code:

#include <vector>

To create a vector that will contain a certain type, e.g., double-precision values:

std::vector<Double_t> myVector;

If you want to create a vector with a fixed number of elements, e.g., 8:

std::vector<Double_t> myOtherVector(8);

To refer to a specific element of a vector, use the same notation that you use for C++ arrays:

myOtherVector[2] = 0.05;

To append a value to the end of the vector, which will make the vector one element longer, use the push_back() method:

myVector.push_back( 0.015 );

To find out the current length of a vector, use the size() method:

Int_t length = myVector.size();

Here’s a simple code fragment that loops over the elements of a vector and prints them out.

for ( size_t i = 0; i != someVector.size(); ++i ) {
    std::cout << "The value of element " << i
              << " is " << someVector[i] << std::endl;
}

You have a vector, but TGraphErrors wants a C++ array name. Here’s the trick:

// Define four vectors.
std::vector<Double_t> x,y,ex,ey;
// Put values in the vectors (omitted so you can do it!)
Int_t n = x.size();
TGraphErrors* plot = new TGraphErrors(n, x.data(), y.data(), ex.data(), ey.data());

In other words, if v has the type std::vector<Double_t>, then v.data() is equivalent to the underlying Double_t array.

We’re getting closer to being able to tackle the first advanced exercise!


1

If you’re doing these exercises in Python: You’ll want to read up on numpy arrays instead. Fortunately, numpy arrays will automatically be converted to C++ arrays when they’re passed as arguments to ROOT methods.

2

If you did this and are puzzled by my description, search the web for the definition of “sarcasm.”

3

In previous versions of this tutorial, I spent a couple of pages discussing object inheritance, and what it means to, e.g., “inherit from TObject.” The new ROOT web documentation makes it harder to determine object inheritance; you often have to actually look at ROOT’s C++ source code. I decided to spare you that as much as possible.

4

I’ve lost track of the number of your lifetimes I’ve spent. You’re probably tired of the joke anyway.