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()のように扱えますね。

Shoichiro Masuoka

CNS, the Univ. of Tokyo. Dcotoral student

関連項目