C++ゆとり用
世の中にはわたしのようなライトC++書き向けの資料が不足しているので,
普段使い*1用途で収集したものをまとめてみました.
ドキュメントを読む
- C++プログラミングガイド
モダン C++ プログラミング - 日本語公開記事 - サイボウズエンジニアのWIKI
- STLのドキュメント
cplusplus.com - The C++ Resources Network
C++ reference - cppreference.com
clangを使う
Clang - Wikipedia
gccよりもclangの方がC++ 規格に準拠しているとうたわれています. また, コンパイルもちょっと速い. *2
標準ライブラリを使う
∧∧∧∧∧∧∧∧∧ < STL!STL!STL!STL! > ∨∨∨∨∨∨∨∨∨ _ _ `/っ) /っ) / / ∧_∧ / / ∧_∧ \\( )\\( )
Standard Template Library - Wikipedia
STLではコンテナとアルゴリズムが分離されており,
用途に応じて適切なデータ構造(コンテナ)に格納されたデータに対してアルゴリズムを適用します.
[STL] Containers
vector,map,list,dequeueなど
hashが見当たりませんが, unorderd_mapが一応それに相当します.
http://www.cplusplus.com/reference/stl/
[STL] stream
iostream,fstreamやstringstreamなど,ほげほげstream群も便利
int toInt(std::string s) { int r = 0; std::istringstream ss(s); ss >> r; return r; } string toStr(int n) { std::ostringstream ss; ss << n; return ss.str(); }
追記
C++11から, std::to_string/std::stoi, stod... が登場した模様 *3
std::stoi, std::stol, std::stoll - cppreference.com
std::to_string - cppreference.com
STLコンテナの使い分け
各コンテナの性質を理解せずに使っているとパフォーマンスが落ちることがあります.
オーダーを理解して適切なコンテナとアルゴリズムを選択しましょう.
[C++] STLの型の使い分け - Qiita
ヘッダファイルでusing namespaceする罪人よ
using namespace std;
ヘッダーを読み込んだファイルが汚染されます. やらかすと殴られます.
たとえばOpenCVはcv::vectorがあってstd::vectorとややこしいので, STL以外にライブラリを使うときは名前空間を常に明示するようにしています.
std::vector<cv::Mat> images;
&参照を使う
余計な一時オブジェクトを作るとメモリが無駄なので, なるべく参照渡しします.
void hogehoge(const std::vector<int>& vec) { std::cout << vec.size() << std::endl; // 実体のように扱えるが, 参照 }
Cだと class* obj とかポインタで受け取ってobj->propのようにアクセスしたり,
実体(コピー)を受けてobj.propのようにアクセスしたりでしたが, &参照が出来るようになって扱いやすくなりました. データを読み出し専用にしたい場合は, const宣言を忘れずに.
constを使う
constのお陰で安心してC++が書けます.
class GeodesicDome { private: const double radius; const int split_depth; public: std::vector<double> vertices; public: GeodesicDome(double radius, int split_depth) : radius(radius), split_depth(split_depth) {}; void createFromIcosaHedron(); void render() const; void dumpStatus() const; void output(const std::string& fname) const; };
変更したくないオブジェクトにはconstを付加し, インスタンス変数を変更しないメソッド宣言末尾にもconstを付加します. こうすると違反をした時にコンパイラが怒ってくれます.
クラスのconstなメンバ変数は, 初期化子で初期化してやる必要があります.
GeodesicDome(double radius, int split_depth) : radius(radius), split_depth(split_depth) {};
また, constなポインタは記述が特殊なので注意が必要です.
結論から言うと
const char* const str="hogehoge";
と書くと実体とポインタともにconstが付与されますが, よくわからないのでもうちょっとみてみます.
int target = 42; // 指し示したいやつ const int* ptr1 = ⌖ int* const ptr2 = ⌖ const int* const ptr3 = ⌖
とすると,*ptrとすれば42が返ってくるのですが,
// ポインタが指し示す先のデータを書き換えようとする *ptr1 = 256; // ERROR *ptr2 = 256; // OK *ptr3 = 256; // ERROR // ポインタを書き換えようとする int dst = 1024; ptr1 = &dst; // OK ptr2 = &dst; // ERROR ptr3 = &dst; // ERROR
となるので, ポインタ型宣言の左側のconstはポインタが指すデータをconstとし,
ポインタ型宣言の右側のconstはポインタに格納されているアドレスをconstとします.
constは外せる
const_castでconstを取ることができます.
無論, フリーダムにconst_castを使用するとプロダクトが崩壊するので, やむを得ない場合に使います.
利用例
void legacyFunc(char* str); ... const char* input="const characters"; legacyFunc(const_cast<char*>(input));
10/02 利用例がそもそもおかしかったので修正しました
inline展開
inline宣言すると呼び出し元に展開されるので, 関数呼び出し時のオーバーヘッドを抑えることができます. 短くて何度も呼び出される関数はinline宣言にすると良いです.
inline double hoge() { //なんかシンプルな処理 return xxx; }
コンパイラの最適化オプションについては
http://www.cqpub.co.jp/interface/column/freesoft/2002/200208/08-4.htm
展開すればするほど実行ファイルのサイズは大きくなり, コンパイルも遅くなるので注意します.
C++11の機能を使う
$ g++ -std=c++11 $ clang++ -std=c++11
[C++11] auto
型推論ができるようになりました.
auto x = 42; // int auto y = 3.14; //float std::vector<int>iterator it = v.begin() // C++03 auto it = v.begin() // C++11
もう長ったらしいイテレータを記述しなくて良いですね.
[C++11] foreach
std::vector<double> vec = { 1,2,3,4,5,6 }; for (const auto& v: vec) { std::cout << v << std::endl; } for (const int x: { 1,2,3,4,5,6 }) std::cout << x << std::endl;
参照&無しで受けると要素をdeep copyで受けます.
[C++11] initializer list
上で出てるけど, 要素の初期化がとても楽になりました.
std::vector<int> v0 = { 1,0,0 }; std::vector<int> v1 = { 1,0,0 }; std::vector<int> v2 = { 1,0,0 }; std::vector<std::vector<int>> v3 = { v1,v2,v3 }; std::vector<std::vector<int>> v4 = { {1,2,3}, {1,2}, {1} };
コンストラクタにも.
return std::vector<int>({1,2,3});
[C++11] array
固定長配列が仲間入りしました.
int ary[5] = {1,2,3,4,5}; // いままでの int ary[] = {1,2,3,4,5}; // いままでの std::array<int,5> ary= {1,2,3,4,5}; // C++11 std::array
09/17 追記
可変長配列を扱うstd::vectorは動的確保を行うのに対して, 固定長配列を扱うstd::arrayは静的確保なので,
int* ary = new int[5]; // 動的確保
は比較として正しくないという指摘を受けて修正しました.
私は殆ど使ってないのですが, 恩恵は, STLコンテナ特有のメソッド(beginやendとか)が使えるようになって扱いやすさが向上&生ポインタを触らなくてよいとかでしょうか.
静的配列なのでvectorよりもメモリ周りの処理がない分高速?
// 今まで int ary[5] = { 9,1,3,4,2}; std::sort( ary, ary+5 ); // 絶対書きたくない // C++11 int array<int,5> ary = {9,1,3,4,2}; std::sort(ary.begin(), ary.end()); // GOOD
[C++11] nullptr
C | C++ | |
NULL | (void*)0 | 0 |
CではNULLはvoid*でしたが, C++ではマクロで0になってるので謎挙動の原因になります.
関数オーバーロードで引数が異なるものがあるとき
void hogehoge(char* foo, int bar); void hogehoge(char* foo, void* bar);
第2引数でNULLをえいやと渡すと, intと解釈されてvoid hogehoge(char* foo, int bar);
の方が呼び出されてしまうので,
hogehoge(_foo, (void*)NULL);
とかvoid*キャストして頑張ってましたが, nullptrを渡せばよくなりました.
NULLはマクロで定義された0ですが, nullptrはぬるぽであることをコンパイラに教えることができます.
[C++11] static_assert
cassertは実行時評価ですがstatic_assertはコンパイル時評価です.
第2引数で文字列をとれるので, ANDで文字列を渡すという変なハックをしなくて良いです
assert(a==b && "[ERROR] a is not equal to b"); // 実行時 static_assert(a==b, "[ERROR] a is not equal to b"); //コンパイル時
[C++11] 暗黙的コピーコンストラクタの禁止
private宣言するとか頑張らなくても, deleteを指定すればコピーコンストラクタの使用を禁止できるようになりました.
class NoCopy { NoCopy(const NoCopy&) = delete; NoCopy& operator=(const NoCopy&) = delete; }
[C++11] ラムダ
ラムダ関数が定義できるようになりました.
[](int x, int y) -> int { int z = x + y; return z + x; } [](int x, int y) { int z = x + y; return z + x; } // return val; の場合は戻り値型を省略できる
[]内は
[] | ラムダ関数外のどの変数も使うことができない。 |
[x, &y] | xはコピーされる。yは参照渡しされる。 |
[&] | すべての外部変数は、もし使われれば暗黙的に参照渡しされる。 |
[=] | すべての外部変数は、もし使われれば暗黙的にコピーされる。 |
[&, x] | xは明示的にコピーされる。その他の変数は参照渡しされる。 |
[=, &z] | zは明示的に参照渡しされる。その他の変数はコピーされる。 |
のように扱えます.
今まで外部関数を宣言していたのが, スマートに書けるように.
これでPerlとかでいうmapとかgrepも, STLのアルゴリズムにラムダ式を放り込んでかけちゃう?*5
#!/usr/bin/perl my $prices = [ 98, 4980, 12500 ]; my $pay = [ map { $_ * 1.08 } @$prices ]; #=> [105.84,5378.4,13500] my $found = [ grep { $_ > 1000 } @$prices ]; #=> [4980,12500]
mapはこんな感じか
// map func const double scale = 1.08; std::transform(v.begin(), v.end(), result.begin(), [scale](int x) -> double {return x*scale });
全然使いこなしてないので, 色んなユースケースを読んでみたい
本の虫: lambda 完全解説
boostを使う
HSTL (HOBO Standard Template Library) ほぼ標準テンプレートライブラリ*6
# Mac OS X $ brew install boost --c++11 # Linux (Ubuntu) $ sudo apt-get install libboost-dev $ sudo apt-get install libboost-xxx # 使いたいライブラリを適宜いれる
まとめて入れたければ libboost-all-devもある模様
Ubuntu – precise の libboost-all-dev パッケージに関する詳細
普段使いにはboost_program_options*7, boost_system などが入って行きやすいです.
boost_regexも使っていましたがC++11からstd::regexが追加されたので使わなくなりました.
http://www.cplusplus.com/reference/regex/
周囲ではboost_threadも使っている人が多いでしょうか.
boostにあったような機能がC++の新規格に組み込まれて標準化され...というのが結構あるので, 試験的ライブラリ群と見ることも
https://sites.google.com/site/boostjp/tips/cxx11-boost-mapping
splitとかいつも自作で悲しいのだが, STLで何故かみあたらない...(実はあるのか・・?)
boostにはstring_algorithmが一応あります.
http://www.boost.org/doc/libs/1_56_0/doc/html/string_algo.html
C++はなるべく書かない
お前ここまで来て何いってんだよという感じですが, 研究ではC++はなるべく書かないようにしています.
勿論どうしてもC++でないと実装できない箇所はC++で作ります.
ただ, LLで書いた方が遥かにコストが低いパーツが多いので, C++で作る部分はコマンドラインオプション*8を受け取ってデータシートを吐く最小の機能にとどめて, LLでラップして回したりしています*9.
後に変更を加えたい時に, C++記述部に変更を加えて全体の再コンパイルを要するのと, LLで記述した部分を変更して再実行すれば良いのとでは, 多くの場合後者の方が楽でしょう.
リファレンス(vim用)
おお, こんな便利なものが...知らなかった
GitHub - rhysd/unite-n3337: A vim plugin for unite.vim to look in N3337 quickly
タブ設定
自分はソフトタブ派ですが, C++のデフォルト幅がスペース4で読み辛いので
エディタの設定で*.cpp *.hのタブ幅をスペース2個にしています.
" vimの場合の設定 augroup IntentGroup autocmd! au BufNewFile,BufRead *.cpp set nowrap softabstop=2 shiftwidth=2 au BufNewFile,BufRead *.cc set nowrap softabstop=2 shiftwidth=2 au BufNewFile,BufRead *.h set nowrap softabstop=2 shiftwidth=2 au BufNewFile,BufRead *.hpp set nowrap softabstop=2 shiftwidth=2 augroup END
あっ
スマートポインタのこととか全然書いてない.....................................
雑談
C++に限らないけど, コールバック関数で固定引数を一部使わずに放置すると
Warning: unused variable xxx:
が出て鬱陶しいときがあります.
# define UNUSED_VARIABLES(x) (void)(x)
そういうときは↑みたいなマクロを定義してvoidキャストで消費してみたりするのだが, 雑なので良い方法が知りたい
09/25 追記 ---
使用しない仮引数 - 職業としてのプログラミング
なるほど!!!!
参考
C++をvimで書くときのtips
Vim で C++ を書くときの逆引きリファレンス - はやくプログラムになりたい
Home · osyo-manga/cpp-vimrc Wiki · GitHub
Effective Modern C++
C++の古典的名著といえばEffecive C++ ですね.
Effective C++ 原著第3版 (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)
- 作者: スコット・メイヤーズ,小林健一郎
- 出版社/メーカー: ピアソン・エデュケーション
- 発売日: 2006/04/29
- メディア: 大型本
- 購入: 29人 クリック: 411回
- この商品を含むブログ (187件) を見る
著者のスコットメイヤーズもこんなことを↓言っていました
The View from Aristeia: Effective C++ in the C++0x (C++11) Age
C++11/14規格をサポートした内容として,Effective Modern C++が出ています.
Effective Modern C++: 42 Specific Ways to Improve Your Use of C++11 and C++14
- 作者: Scott Meyers
- 出版社/メーカー: O'Reilly Media
- 発売日: 2014/11/10
- メディア: Kindle版
- この商品を含むブログ (1件) を見る