C++/Memo
- 雑多なメモを書く。WEB 上にある先人達のメモがとても参考になる。
テキストファイルの読み込み/文字列、数値の取得
- テキストファイルの読み込み
- substr の例外
- try とか catch を使って、std::out_of_range を取得する?
- プログラム初心者に読みづらそうなコードになりそうであるし、ROOT のマクロで使えるかよくわからないので、やめた。
プリプロセッサ
プリプロセッサの行にコメントって書いてよいんだろうか?C# だと/* */ はダメ? 参考: プリプロセッサ ディレクティブ
その他
vector が持つ配列へのアクセスとそのポインタへのアクセス (参考: HongoWiki - C++)
std::vector<int> vec;
- 配列の要素へのアクセス: vec[0] / vec.at(0)
配列のポインタへのアクセス: &(vec[0]) / &(vec.at(0))
std::vector<int> *vec; = new std::vector<int>();
配列の要素へのアクセス: (*vec)[0] / vec->at(0)
配列のポインタへのアクセス: &((*vec)[0]) / &(vec->at(0))
- vector の持つ配列のアドレスは push_back() メソッドで変わる。
- ROOT のマクロで確認
#include <iostream> #include <vector> int vec_adr(){ std::vector<int> v1(1); std::vector<int> *v2 = new std::vector<int>(); for (int i = 0; i<100; i++) { v1.push_back(1); std::cout << "v1: " << &(v1[0]) << ", " << &(v1.at(0)) << std::endl; } for (int i = 0; i<100; i++) { v2->push_back(1); std::cout << "v2: " << &((*v2)[0]) << ", " << &(v2->at(0)) << std::endl; } return 0; }
- 実行結果
v1: 0x14c45c0, 0x14c45c0 v1: 0xae1de0, 0xae1de0 v1: 0xae1de0, 0xae1de0 v1: 0x15d09e0, 0x15d09e0 v1: 0x15d09e0, 0x15d09e0 v1: 0x15d09e0, 0x15d09e0 v1: 0x15d09e0, 0x15d09e0 v1: 0x14c46f0, 0x14c46f0 ... v2: 0xae1de0, 0xae1de0 v2: 0x14c45c0, 0x14c45c0 v2: 0xae1de0, 0xae1de0 v2: 0xae1de0, 0xae1de0 v2: 0x15d09e0, 0x15d09e0 v2: 0x15d09e0, 0x15d09e0 v2: 0x15d09e0, 0x15d09e0 v2: 0x15d09e0, 0x15d09e0 v2: 0x14c46f0, 0x14c46f0 ...
- ポインタの参照渡し
- TH1D クラスの Clone を関数内で作ってポインタで返すときの参考
- コンストラクタでは、代入よりも初期化を使うのが良いらしい
- 代入の方は、計算コストがかかるらしい。そもそもメンバ変数が const の場合、初期化子リストでしか初期化できないらしい。そういえば、const でない普通のメンバ変数の宣言時に値を代入しようとしても駄目だった。
- 初期化子リストの順番はメンバー変数の宣言の順番にしないと警告が出る。
Info in <TUnixSystem::ACLiC>: creating shared library /home/koba/exp/sunday/anaroot/./DataTree2RawTree_C.so In file included from /home/koba/exp/sunday/anaroot/DataTree2RawTree_C_ACLiC_dict.h:34, from /home/koba/exp/sunday/anaroot/DataTree2RawTree_C_ACLiC_dict.cxx:17: /home/koba/exp/sunday/anaroot/./DataTree2RawTree.C: In constructor‘LoadMap::LoadMap(TTree*/hokoba/exp/sunday/anaroDataTree2RawTree.C:151: 警告: ‘LoadMap::valarr’ will be initialized after /home/koba/exp/sunday/anaroot/./DataTree2RawTree.C:148: 警告: ‘Int_t LoadMap::num_v’ /home/koba/exp/sunday/anaroot/./DataTree2RawTree.C:76: 警告: when initialized here
- 文字列の split は以下が参考になる。
参考: C++で文字列のsplit
- メモリの静的確保と動的確保
- 基本的に静的確保で事が足りれば、静的確保を使う。動的確保をした場合、ちゃんと自分で開放しなければメモリリークとなるため、面倒。
- 動的確保をする場合(new 演算子を使って)
- コンパイル時にメモリをどのくらい使うか分からない場合
- 例えば、読み込むファイルの行数が不定で、コンパイル時にその行数がわからない場合、読み込んだ内容を入れておく変数は動的確保をせざるを得ない。(十分におおきな領域を確保しておく方法もある。)
- インスタンスを残しておきたい場合?
- スコープを抜けてもインスタンスを残しておきたい場合、new する?
- クラスの引数として new したインスタンスを要求する場合?
例えば、Geant4 の G4RunManager には、Physics List やディテクターのインスタンスを new してから登録するが、G4RunManager のインスタンスが delete されるときに、一緒に delete されているようだ?new したものを必要とするインスタンスがある?
- コンパイル時にメモリをどのくらい使うか分からない場合
- std::string (変数名は str) 中 の "," と ";" と ":" を " " (スペース) に置換
1 std::string str = "aa, bb; cc : dd"; 2 std::cout << str << std::endl; 3 std::string::size_type pos = 0; 4 while(pos = str.find_first_of(",;:", pos), pos != std::string::npos) { 5 str.replace(pos, 1, " "); 6 pos++; 7 } 8 std::cout << str << std::endl;
- 表示結果
aa, bb; cc : dd aa bb cc dd
- ただし、ROOTのマクロだと使えない。while の括弧の中に , を書けないため。以下のように書き換える。
1 std::string str = "aa, bb; cc : dd"; 2 std::cout << str << std::endl; 3 std::string::size_type pos = 0; 4 while((pos = str.find_first_of(",;:", pos)) != std::string::npos) { 5 str.replace(pos, 1, " "); 6 pos++; 7 } 8 std::cout << str << std::endl;
- std::string (変数名は str) 中 の "," と ";" と ":" を 削除 (置換の改変バージョン)
1 std::string str = "aa, bb; cc : dd"; 2 std::cout << str << std::endl; 3 std::string::size_type pos = 0; 4 while((pos = str.find_first_of(",;:", pos)) != std::string::npos) { 5 str.replace(pos, 1, ""); 6 // pos++; // 1文字分削除しているので、インクリメントの必要なし 7 } 8 std::cout << str << std::endl;
- 二次元配列の動的確保
- 二次元配列を new 演算子を使って動的確保するには、一次元ずつ確保する。ただし、静的確保された通常の二次元配列の場合、メモリは連続的に確保されるが、動的確保の場合、連続して確保されているとは限らない。しかし、配列のメモリが連続的に確保されている事を前提に、ポインタ一つで全体を参照したりしたことはないので、特に問題ないかも?関数に配列を渡すときはポインタを渡すが、まるごと渡したいときは注意が必要?
- 可変長引数について
- 可変長引数を使ったときの警告
/home/koba/exp/sunday/anaroot/./DataTree2RawTree.C: In member function ‘void LoadMap::Read(std::string, Int_t, ...)’: /home/koba/exp/sunday/anaroot/./DataTree2RawTree.C:92: 警告: second parameter of ‘va_start’ not last named argument
- 以下のようなコードだと警告が出る。
1 void Read(int num, ...) { 2 int tmp = num; 3 va_list ap; 4 va_start(ap, tmp); 5 ...
- 警告のメッセージが指摘する通り、va_start(ap, tmp); のtmp を num に変えれば警告はでない。
参考: How to remove this warning: second parameter of ‘va_start’ not last named argument?
- map の全要素にアクセス
1 std::map<std::string, Int_t>::iterator it = mapid.begin(); 2 while (it != mapid.end()) { 3 std::cout << (*it).first << ":" << (*it).second << std::endl; 4 it++; 5 }
- std::ostringstream はさすがにループ内などで宣言と破棄を繰り返すと、時間がかかる。
1 for (int i = 0; i < 1000; i++) { 2 std::ostringstream oss; 3 oss << aa << bb; 4 std::cout << oss.str() << std::endl; 5 }
- 上より下記のほうがよさげ?
1 std::ostringstream oss; 2 for (int i = 0; i < 1000; i++) { 3 oss.str(""); 4 oss.clear(); 5 oss << aa << bb; 6 std::cout << oss.str() << std::endl; 7 }
- file stream からの読み込み
- ファイルからデータを読み込む時、以下のようなコードを書いていた。file stream (?) を作り、一行づつ string に入れ、その一行を string stream に入れて、さらにそこからデータを引っ張ってきていた。
1 #include <fstream> 2 #include <sstream> 3 4 ... 5 std::ifstream ifs("file.dat"); 6 std::string tmpstr; 7 while (getline(ifs,tmpstr)) { 8 std::istringstream iss(tmpstr); 9 double a, b, c; 10 iss >> a >> b >> c; 11 a, b, c に対して処理 12 ... 13 } 14 ...
しかし、ちゃんと整形されたデータが格納されたデータファイルならば、以下のように書けばよいらしい(参考 : ROOT講習 - フィット)。
1 #include <fstream> 2 3 ... 4 std::ifstream ifs("file.dat"); 5 double a, b, c; 6 while (ifs >> a >> b >> c) { 7 a, b, c に対して処理 8 ... 9 } 10 ...
- string の中身が数値か判定
- string の中に 数字や符号以外の文字が含まれているかを調べれば良い。ただし、"10-40" といった文字列も数字とみなされる。
1 #include <string> 2 3 // std::string str の中身が整数かどうか 4 5 if(str.find_first_not_of("-+0123456789") == string::npos) { 6 std::cout << "str : integer" << std::endl; 7 } 8 9 // std::string str の中身が実数かどうか 10 11 if(str.find_first_not_of("-+0123456789.Ee") == string::npos) { 12 std::cout << "str : real" << std::endl; 13 }
- C++98 では {} を使って vector に要素を代入できない
/home/koba/exp/sunday/anaroot/./RawTree2CalTree.C:303: error: in C++98 ‘strvec1’ must be initialized by constructor, not by ‘{...}’
- クラスを実体、またはポインタで宣言
- クラスをポインタで宣言(?)すると、宣言時にコンストラクタは呼ばれない。実体で宣言すると、コンストラクタが呼ばれる。ポインタで宣言するか、実体を宣言するかの判断基準がまだよくわかっていないが、これは参考にした方が良いかも。
MyClass *myclassptr; // コンストラクタは呼ばれない MyClass myclassins; // コンストラクタが呼ばれる
- 未確保 の std::vector の要素に [] 演算子でアクセスしても、変な値が返されるだけで、segmentation violation にならない(ときがある?)。
[] 演算子 で vector の要素にアクセスしてから push_bash をすると、以下の感じのエラーが出るかも?参考(?):“glibc free(): invalid next size(fast)” on vector.push_back?
*** glibc detected *** /mnt/misc/sw/x86_64/Debian/7/root/gnu/5.34.09/bin/root.exe: free(): invalid next size (fast): 0x0000000002e20300 ***
エラーを出したマクロ: plot_levels.C (入力ファイルの第一励起状態のエネルギーが低くいとエラーになった気がする。)
インプットファイル: sdpfm.txt, sdpfmu.txt