_ROOT Version:v6.24.06
Platform: MacOS “Big Sur” 11.6.5
Compiler: Apple clang version 13.0.0 (clang-1300.0.29.30)
I regularly fight segmentation faults and I’d like to lessen the amount of those bugs I have to fight by having good habits in the way I do things and one question I have is a comparison of using new with and without a variable that I handle to store that address if I’m using something from ROOT that is a type of container.
I would love to write a general case using TList, but I don’t handle them bare, so I’m using TMultiGraph as it contains multiple ROOT objects of type TGraph. In an answer, please regard if the behavior is because of TList and I should expect it for all containers that rely on TList.
With that preface, my question is: Which of the following is “best”?
Or is this always an application specific question? Maybe instead: What are relative dis/advantages and respective use cases between these implementations?
Note for all the below code:
- goal is to plot given data using a TMultiGraph
- the code shown below is at function scope
- let there be 2 variables at a much higher scope (possibly global)
- the TMultiGraph pointer,
auto mg = new TMultiGraph()
- the data,
vector<pair<vector<Double_t>,vector<Double_t>>> xy_data
- the TMultiGraph pointer,
Method 1 (user-based): Most user-based memory management
// allocate
std::vector<TGraph*> g_list(n_graphs);
for (const auto& [x, y] : xy_data){
// init data for graph i
g_list[i] = new TGraph(x.size(), x.data(), y.data());
}
// associate to ROOT container
for (const auto& g : g_list){
mg->Add(g);
}
mg->Draw();
// free
for (const auto g : g_list)
delete g;
Method 2 (STL-based): Rely on STL for containers
// allocate TMultiGraph and TGraphs but let stl handle memory for TGraphs
std::vector<TGraph> g_list();
for (const auto& [x, y] : xy_data){
// init data for graph i
g_list.emplace_back(x.size(), x.data(), y.data()));
}
// associate to ROOT container
for (const auto&& g : g_list){
mg->Add(&g);
}
mg->Draw();
// frees at end of scope with STL ~vector<>
Method 3 (ROOT-based): Rely on ROOT containers completely (not sure what it will/won’t do)
// associate to ROOT container
for (const auto& [x, y] : xy_data){
// init data for i-th TGraph
mg->Add(new TGraph(x.size(), x.data(), y.data()));
}
mg->Draw();
// ~TMultiGraph frees when mg goes out of scope
Method 1 requires user to delete
which is a chance for memory leaks. I personally want to avoid this. But I see the advantage of having the each TGraph easily accessible if I wanted to pass it to another function before I called mg->Draw()
. I see this style very often.
Method 2 doesn’t require user to delete
anymore. It has overhead to store the list of TGraphs, but again, I have a the TGraphs easily accessible. I don’t see this often.
Method 3 is pure ROOT, so I feel like it’s what I should be using every time, BUT I don’t see this at all and I feel like that’s not good, the destructor for TList looks like it wants it to be done this way.
What should I aim to do?
And a last note, why might I want to have mg
not be dynamic memory and just have mg
be of type TMultiGraph
?