Aritalab:Lecture/Programming/Cpp/Tips
m (→タブ区切りテキストを読むにはstringを使う) |
|||
(10 intermediate revisions by one user not shown) | |||
Line 1: | Line 1: | ||
− | + | {{Lecture/Header}} | |
− | ; | + | ここではC++プログラムのコツを順次まとめています。 |
− | + | ||
+ | ==マクロは書かない== | ||
+ | Cプログラマがマクロを使って得られたメリットのほぼ全てが、テンプレートやconst指定を使って実現可能です。 | ||
+ | 型チェックをすり抜けるマクロの利用はできる限り避けてください。 | ||
+ | マクロをどうしても書きたい人は、[[Aritalab:Lecture/Programming/Cpp/Macro|書き方のページ]]を参考にしてください。 | ||
+ | |||
+ | ==メモリ管理== | ||
+ | ===巨大な配列はヒープ領域に確保する=== | ||
+ | |||
+ | {| | ||
+ | !width="40%"| Good | ||
+ | !width="40%"| Bad | ||
+ | |- | ||
+ | | | ||
<pre> | <pre> | ||
− | + | /* 問題なく動く */ | |
− | + | int* array = new int[1000000]; | |
</pre> | </pre> | ||
− | + | | | |
+ | <pre> | ||
+ | /* overflowを起して動かない */ | ||
+ | int array[1000000]; | ||
+ | </pre> | ||
+ | |} | ||
+ | |||
+ | |||
+ | プログラムが利用するメモリには、new/delete(malloc/free)で確保されるヒープ領域と、関数呼び出しや変数の利用で確保されるスタック領域があります。 | ||
+ | |||
+ | 通常、ヒープ領域には大きなスペースが確保されていますが、スタック領域は1MB程度しかありません。プログラム中で大きな配列を(自動変数として)定義するとすぐにStack overflow errorを起こします。大きな配列はnewで確保しましょう。 | ||
− | + | ===イテレータのインクリメントは前置にする=== | |
− | < | + | {| |
− | + | !width="40%"| Good | |
+ | !width="40%"| Bad | ||
+ | |- | ||
+ | | | ||
+ | <pre> | ||
+ | vector<string> V; | ||
+ | : | ||
+ | for(vector<string>::iterator itr=V.begin(); | ||
+ | itr != V.end(); ++itr) | ||
+ | cout << *itr << "\n"; | ||
+ | </pre> | ||
+ | | | ||
+ | <pre> | ||
+ | vector<string> V; | ||
+ | : | ||
+ | for(vector<string>::iterator itr=V.begin(); | ||
+ | itr != V.end(); itr++) | ||
+ | cout << *itr << "\n"; | ||
+ | </pre> | ||
+ | |} | ||
+ | 基本データ型でない場合、インクリメント演算子の定義は以下のようになります。戻り値の型に注意してください。後置オペレータは処理の後にインクリメントするため、返り値をインクリメントする前の状態にしないといけません。 | ||
+ | {| | ||
+ | |width="40%"| | ||
<pre> | <pre> | ||
iterator operator++(int) | iterator operator++(int) | ||
Line 17: | Line 62: | ||
iterator _Tmp = *this; | iterator _Tmp = *this; | ||
++*this; | ++*this; | ||
− | return _Tmp; | + | return _Tmp; // ここでインクリメント前の値を返す |
} | } | ||
Line 26: | Line 71: | ||
} | } | ||
</pre> | </pre> | ||
− | + | |} | |
− | + | ||
+ | この理由で、後置オペレータではオーバーロードする関数内でコピーコンストラクタによるオブジェクトの作成と、作成したオブジェクトを戻す際に再びコピーと、余分にオブジェクトが作成されるのです。 | ||
+ | 余分な処理を除くため、前置インクリメントを使います。(ただし、基本データタイプについてはどちらでも効率は同じです。) | ||
+ | |||
+ | ==ファイル入出力== | ||
+ | |||
+ | ===ファイル全体を読むときはgetlineで行毎に=== | ||
+ | ファイル全体をEOF (end of file) まで読むときは、while 文を使うと便利です。 | ||
+ | <pre> | ||
+ | std::ifstream fin("data.txt"); | ||
+ | char line[100]; | ||
+ | while(!fin.getline(line, 100, '\n').eof()) | ||
+ | { | ||
+ | //process the line | ||
+ | } | ||
+ | </pre> | ||
+ | getline関数の引数二つ目は読み込む文字数の最大値-1, 三つ目は区切り文字(デリミタ)です。 | ||
+ | 最大文字数から-1されている理由は、必ず最後にヌル文字を入れるからです。 | ||
+ | 三つ目の引数は省略できます。省略すると改行コードを区切り文字にします。(つまり上の例では同じ動作になる。) | ||
+ | |||
+ | ===区切りテキストを読むにはstringを使う=== | ||
+ | std::getline関数はstringクラスを引数にとれます。例えば、 | ||
+ | <pre> | ||
+ | データ1 データ2 データ3 | ||
+ | データ4 データ5 | ||
+ | 次のデータセット | ||
+ | </pre> | ||
+ | という2行毎のタブ区切りデータがある場合、以下のwhileループで読めます。 | ||
+ | <pre> | ||
+ | #include <string> | ||
+ | using namespace std; | ||
+ | ifstream fin("data.txt"); | ||
+ | string data1; | ||
+ | while (getline(fin, data1, '\t').eof()) | ||
+ | { | ||
+ | string data2, data3, data4, data5; | ||
+ | getline(fin, data2, '\t'); // タブまで読む | ||
+ | getline(fin, data3); // 改行まで読む | ||
+ | getline(fin, data4, '\t'); // タブまで読む | ||
+ | getline(fin, data5); // 改行まで読む | ||
+ | ... | ||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | ===改行はendlではなく、"\n"を出力する=== | ||
+ | {| | ||
+ | !width="40%"| Good | ||
+ | !width="40%"| Bad | ||
+ | |- | ||
+ | |<pre>out << "\n";</pre> | ||
+ | |<pre>out << endl;</pre> | ||
+ | |} | ||
+ | <tt>endl</tt>を出力する操作は、<tt>out << "\n"; out.flush();</tt>と同じです。頻繁に利用する場合は、flushの分だけ前者のほうが効率が良くなります。 |
Latest revision as of 17:26, 19 October 2010
Wiki Top | Up one level | レポートの書き方 | Arita Laboratory |
|
ここではC++プログラムのコツを順次まとめています。
[edit] マクロは書かない
Cプログラマがマクロを使って得られたメリットのほぼ全てが、テンプレートやconst指定を使って実現可能です。 型チェックをすり抜けるマクロの利用はできる限り避けてください。 マクロをどうしても書きたい人は、書き方のページを参考にしてください。
[edit] メモリ管理
[edit] 巨大な配列はヒープ領域に確保する
Good | Bad |
---|---|
/* 問題なく動く */ int* array = new int[1000000]; |
/* overflowを起して動かない */ int array[1000000]; |
プログラムが利用するメモリには、new/delete(malloc/free)で確保されるヒープ領域と、関数呼び出しや変数の利用で確保されるスタック領域があります。
通常、ヒープ領域には大きなスペースが確保されていますが、スタック領域は1MB程度しかありません。プログラム中で大きな配列を(自動変数として)定義するとすぐにStack overflow errorを起こします。大きな配列はnewで確保しましょう。
[edit] イテレータのインクリメントは前置にする
Good | Bad |
---|---|
vector<string> V; : for(vector<string>::iterator itr=V.begin(); itr != V.end(); ++itr) cout << *itr << "\n"; |
vector<string> V; : for(vector<string>::iterator itr=V.begin(); itr != V.end(); itr++) cout << *itr << "\n"; |
基本データ型でない場合、インクリメント演算子の定義は以下のようになります。戻り値の型に注意してください。後置オペレータは処理の後にインクリメントするため、返り値をインクリメントする前の状態にしないといけません。
iterator operator++(int) { //後置 iterator _Tmp = *this; ++*this; return _Tmp; // ここでインクリメント前の値を返す } iterator& operator++() { //前置 ++*this; return *this; } |
この理由で、後置オペレータではオーバーロードする関数内でコピーコンストラクタによるオブジェクトの作成と、作成したオブジェクトを戻す際に再びコピーと、余分にオブジェクトが作成されるのです。 余分な処理を除くため、前置インクリメントを使います。(ただし、基本データタイプについてはどちらでも効率は同じです。)
[edit] ファイル入出力
[edit] ファイル全体を読むときはgetlineで行毎に
ファイル全体をEOF (end of file) まで読むときは、while 文を使うと便利です。
std::ifstream fin("data.txt"); char line[100]; while(!fin.getline(line, 100, '\n').eof()) { //process the line }
getline関数の引数二つ目は読み込む文字数の最大値-1, 三つ目は区切り文字(デリミタ)です。 最大文字数から-1されている理由は、必ず最後にヌル文字を入れるからです。 三つ目の引数は省略できます。省略すると改行コードを区切り文字にします。(つまり上の例では同じ動作になる。)
[edit] 区切りテキストを読むにはstringを使う
std::getline関数はstringクラスを引数にとれます。例えば、
データ1 データ2 データ3 データ4 データ5 次のデータセット
という2行毎のタブ区切りデータがある場合、以下のwhileループで読めます。
#include <string> using namespace std; ifstream fin("data.txt"); string data1; while (getline(fin, data1, '\t').eof()) { string data2, data3, data4, data5; getline(fin, data2, '\t'); // タブまで読む getline(fin, data3); // 改行まで読む getline(fin, data4, '\t'); // タブまで読む getline(fin, data5); // 改行まで読む ... }
[edit] 改行はendlではなく、"\n"を出力する
Good | Bad |
---|---|
out << "\n"; |
out << endl; |
endlを出力する操作は、out << "\n"; out.flush();と同じです。頻繁に利用する場合は、flushの分だけ前者のほうが効率が良くなります。