TClonesArrayのConstructedAtなどの挙動について
今回はROOTのクラス、TClonesArrayについての挙動が分かってきたのでここでまとめたいと思います。
TClonesArrayはROOTのオブジェクトを複数個Loopで回して作る時に、newをせずにそれを作ることが出来るものです。 このコンストラクタを使うことで例えば
TClonesArray arr("TH1D",10000);
for( Int_t i = 0; i != 10000; ++i) {
new(arr[i]) TH1D(Form("hist%d",i),"hist",10,-0.5,9.5);
static_cast<TH1D*>(arr[i])->Fill(i);
}
とかやってTClonesArrayを初期化してLoopの中でデータを詰めると,このオブジェクトは
TH1D *h5 = static_cast<TH1D*>(arr.At(5));
h5->Draw(); // 4.5~5.5のbinに立つHistogramがDrawされる
という感じで煮るなり焼くなりできます(戻り値はTObject*なので、自身でcastして使用してください。型はTClonesArrayの方で保証されているのでstatic_castで良いと思います)。ROOTでオブジェクトの配列を作るのには自分でnewするのではなくTClonesArrayを使った方がコストもかからずに良いのですね。
使い終わったらarr.Clear("C");
としてメモリクリアをしましょう。この”C”オプションはTClonesArrayのクラスの指定先のClear()を呼ぶので良いらしい。
ハマったところ
さて、このTCloneArray、最初にサイズを指定することなく動的に取得することも可能です。そのときに使用するのがTClonesArray::ConstructedAt(Int_t index)
という関数です。これの使い方にハマってしまいました。
このこ、indexに例えば2を突っ込むとindexのみのオブジェクトを確保するのです。例えば
TClonesArray *arr = new TClonesArray("TH1D");
arr->ConstructedAt(2);
arr->At(0); //null pointerが返ってくる
自分が勝手にConstructedAt(2)を呼べば0,1のオブジェクトも作ってくれると勘違いしていました。関数の名前を見ればそれはそうですよねー……
Event毎に処理をしたいコードに、予め作りたいサイズが分かっている時など、TClonesArrayをResizeをしたいときはTClonesArray::ExpandCreate(Int_t size)
を使用するといい感じ。
また、std::vectorのpush_back()
みたいに、動的にサイズを増やしていきたいときは
Int_t size = arr->GetEntriesFast(); // 例えば、初期化直後だと0が返ってくる
arr->ConstructedAt(size); // 0番目を作る
とするのが常套手段です。ここでこの直後にarr->GetEntriesFast();
を叩くとサイズがインクリメントされるので1が返ってきます。これを繰り返せばpush_back()
のように扱えますね。