プログラミング
C
概要 †
下位ページ †
目次 †
C/C++の発達段階 †
段階 | 許される開発 | 習得すべき技術 | 入出力がきちんと動いているかも確認しない | | | ソースコードコピペをしない | | 再利用性が死んでると,ファイルごとコピーみたくなって,メンテナンス不能なので,マジそれだけはやめて | 入出力がきちんと動くことを1回確認する | | | メモリバイオレーションしない+未初期化変数を使わない | 共同開発で必須 | valgrind, cppcheck, コーディングルール.-O0でメモリクリーンを保証できないなら,-O3する資格はない. | 変数名をきちんとつける | 共同開発で必須 | コーディングルール | アサートを入れる | | コーディングルール | try-catchする | | | コメントを適切につける(意味のないのはNo) | 共同開発で必須 | コーディングルール | 入出力がきちんと動くことをスクリプトで自動確認する=擬似テスト駆動 | 共同開発と大規模開発で必須 | 問題が起きた時にテストを追加していく+サンプルと同様の役割を果たす | メモリリークしない | 組み込み・OS・サーバアプリケーション | | 網羅的な入出力できちんと動くことをスクリプトで自動確認 | | テスト駆動=基本的には費用対効果で,網羅するかが決まる.UIとか無理なのもある | 変数名,コメント以外のドキュメンテーション | 外部ライブラリで必須 | | セキュアプログラミング | 外部ライブラリで必須 | fortify, コーディングルール |
- 組織の習熟も必要
- C++は能力のある人じゃないと書けない,だけではなく,能力のある組織でないと書けないが,C++を書いていいくらいの組織の能力とはなにか.
- その時代ごとにルールは違う.オブジェクト志向が出ていなかったころのルールと今のルールは違う.
- もちろん言語ごとにもルールは違う(C++じゃないとvalgrindとか言う必要ないし).
- コーディングルール,Assert, テスト,valgrind, gdbなどいろいろありすぎるが,全部まさかやっていないよね.
Tips †
イディオム †
操作 | プログラム | 備考 | there exists m in N n = 2^m | long long n; n & (n-1) | | iのn bit目を参照 | !!(i&(1<<n)) | | iのn bit目に1を書き込み | i|=(1<<n)) | | iのn bit目に0を書き込み | i&=^(0<<n)) | | 文字列が初めてマッチしたインデックスの取得 | string s; int i = s.find(to_find_string); | find_first_ofではないので注意!見つからなかったらstring::npos(-1) | 多次元配列の初期化 | int dp[2][50][1010]; memset(dp, 0, sizeof(dp)); | | sumしながらmodもする | (sum += a[i]) %= MOD | |
データ型 | 関数 | reverse(begin(x), end(x)); sort(begin(x), end(x)); find(begin(x), end(x); | string, vector, 配列などに使える | string.find(string) | 文字列から文字列を検索して,初めにマッチした添字を返す.STLのfindは文字列から文字の検索にしか使えない | vector<int>({0, 0}) | 簡単なvectorの作り方 |
STL †
vector †
strstream †
map, reduce, filter †
- サンプルコード
- transform(1変数版,2変数版がある), accumulate(1変数版のみ), copy_ifが対応
- 上書きしないで,新たに変数を作って書き込む場合は,back_inserterを使う
- 2変数版accumulateは作ってしまえという感じある
- あとiteratorを使いたい場合にも適用不能
量化関数 †
文法 †
pure virtual †
- 純粋仮想にはvirtual = 0としなければならない。
- それを引く仮想にも、virtualとしなければならない。
ゲッタ・セッタの生成プログラム †
- これに"int test"などを入力すると,ゲッタとセッタを出力してくれる
awk '{printf("%s get%s(void){return %s;}\nvoid set%s(%s %s){%s = %s;}\n", $1, toupper(substr($2, 1, 1)) substr($2, 2), $2, toupper(substr($2, 1, 1)) substr($2, 2), $1, $2, $2 "_", $2);}'
- 本当はvim scriptでやりたいのだが…
newと実体の動作の違い †
- 完全にコピーコンストラクタがおかしい以外考えられない。
- new delete newで正常動作して、実体定義、実体代入で正常動作しない
親のコンストラクタを呼ぶ †
decisionCross::decisionCross(StockDayStream* stock, int s, int m, int a)
: decision(stock), _s(s), _m(m), _a(a)
{
}
sstreamとstrstream †
- str()が,sstreamはstd::string型を出力し,strstreamはchar*を出力する
- strstreamはstd::endsを最後に入れる必要があるが,sstreamは入れてはならない.
coutに表示させる †
#include <iostream>
class HoldingStock {
public:
int m_code;
std::ostream& operator()(std::ostream& ros) {
ros << m_code << " " << m_code << " " << m_sl << " " << m_tp;
return ros;
}
};
isnanがあいまい †
absは危険 †
fstreamのパターン †
std::ifstream fin(args_csv);
std::string line;
while (std::getline(fin, line)) {
std::string token;
std::istringstream stream(line);
while (std::getline(stream, token, ',')) {
std::cout << '[' << token << ']' << std::endl;
}
}
知識 †
知識名 | サンプル | 備考 | 親クラスがテンプレートクラスである場合、そのメソッドを呼ぶときにはthis->を明示 | | http://d.hatena.ne.jp/tkng/20110419/1303176182 | using namespace Namespace;はclassの中に書けない. | | | using Namespace::Classname;ではClassnameでアクセスできない. | | | using RoboLink? = ::Link;はclass public: の中になら突っ込める | | | constメソッドは,constメソッドを持つクラスのメンバ変数を変更しない | | 形式的には,「constメンバ関数内から見た,メンバ変数は実体の場合&aがconst T*型となり,ポインタの場合aがT* const型となる.」と考えるとよい.例えば class hoge {int a; int* getA const { return &a; } };は,constメンバ関数内では&aがconst int*型なのに,返り値がint*なのでアウト,とか.(constメソッドは,constメソッドを持つクラスのメンバ変数を変更しない.クラスが有するHoge型メンバ変数aに対しては,aの中身の無変更を保証するために&aがconst T*型となる.一方,constメソッドを持つクラスが有するHoge*型メンバ変数aPtrに対しては,aPtr自体の無変更を保証する一方,aPtrの中身が変わるどうかは知ったことではないので,int* const a;とポインタ自体の変更を認めなくする.) | 「constメンバ関数では、暗黙的に渡されるthisポインタがconstで修飾される」が最も基本的 | | ISO/IEC 14882:2003, 9.3.2 The this pointer[class.this] | 静的メンバ関数で,staticを付けるのは定義のみで,実装には付けてはならない | | | 引数1コのみ持つコンストラクタは,暗黙のうちにコピーコンストラクタも定義 | | Hoge(int hoge) { this->hoge = hoge; }はHoge hoge = 10; | 引数1コの暗黙のコピーコンストラクタはexplicitで抑制できる | | | range based forを使っても良い | | for(auto& x : v) { x *= 2; } C++17の話も http://qiita.com/rinse_/items/cdfce8aa6a685a8ebe0c http://imoz.jp/note/cpp11.html http://dtyazsk.blog.shinobi.jp/c--/c--11%E8%B6%85%E5%85%A5%E9%96%80 http://minus9d.hatenablog.com/entry/20130815/1376557665 | namespaceのエイリアスをすると長い名前を短縮できる | | namespace shortname = veryverylongnamespace;namespace netque = torutk::network::que; | namespaceの完全記述 | | using std::endl; using std::cout; using std::cerr;はよく使う | ヘッダファイル中にusingディレクティブを使用するのは禁止 | | | 名前空間を複数の場所で定義しても、それらは同一 | | | using宣言は,「それが書かれたスコープに」そのシンボルを追加する. | | 名前空間そのものを取り込むわけではなくあくまでシンボルを取り込む | グローバル名前空間は、namespaceのブロックに囲まれていない場所で,::でアクセス | | char path[::FILE_PATH_MAX]; | クラス内に効果を持つusingは実はできない | | やりたいならnamespace E { using namespace std; /*OK*/ class Hoge { public: string s;};} http://blog.wizaman.net/archives/542 | Cのconst int* const a;とは,前のconstは中身が変わらないという意味,後のconstはポインタ自体が変わらない | | 前のconstは int const * const としても良い | 静的変数・メソッドはアクセス制限のあるグローバル | | 静的メンバ変数はオブジェクトに属するメンバ変数ではなく、クラスに属するメンバ変数です。アクセス制限が設定されていることを除けば、グローバル変数と同じ |
- コーディングルール
|C++では,Cのポインタのセマンティクスがシンタックスに対しことて多義的なので,スマートポインタ群を使うべき||安全性と開発速度の向上が見込めるだけでなく、プログラマの意図に合わせて「ポインタ」を記述し分けることができる.基本的には全部shared pointerで,外に出すときはweakにして責任がないことを明示し,クラス内やメソッド内の限られたスコープの場合はuniqueにする.uniqueだったものを外に出したくなる場合は当然ある.そういう時にはuniqueのポインタをsharedに書き換える.速度は?スマートポインタのいいサンプル集http://program.station.ez-net.jp/special/handbook/cpp/syntax/smart-pointer.asp http://qiita.com/hmito/items/db3b14917120b285112f, make_sharedを使うと良い, スマートポインタとデザインパターンの連携
unique_ptrの使い方実例|
バージョン管理をnamespaceで実現可能 | | ライブラリ側のコードnamespace XXLib {namespace V1 {バージョン1の機能} namespace V2 {バージョン2の機能} } ライブラリ使用者側のコードnamespace Library = XXLib::V2; Library::func(); // バージョン2の func() を呼び出す | using namespaceは安易に使わない.特にusing namespace std; | | std空間の全ての変数を把握したり、拡張を予期するのは無茶 | C++ではcstdioのようにすることによって名前空間に包んだ安全な関数を呼び出せる | | | C++では,メンバ関数の初期化をhppで行うべき | | 全てのメンバ変数が少なくとも未初期化でないことが保証できるので嬉しい.速度を超攻めるときはこの限りではない.http://qiita.com/YYSS_101/items/cbd06352e047116359ce | Cの生ポインタは,誰が開放責任を持つかは自明ではないので,必ず開放責任をコメントを書く | | | 所有とは、リソースの所有者が(1)自身が所有しているリソースは所有者以外の者からは決して解放されないという権利と(2)自身が所有しているリソースを最後に解放するという義務を負うこと | | | C++11の時点で生配列は非推奨 | | 一般的に静的配列にはstd::array、動的配列にはstd::vectorが使われています。 | ライブラリは,絶対分かるassertを吐いて死ぬ(Eigen型)か,return 1のエラーコードを返して死なない | | ライブラリは絶対勝手に死んではならない |
知識名 | サンプル | 備考 | C++はセマンティクスとシンタックス,から統一的に理解できる | | castは同じシンタックスで複数のセマンティクスが可能だった.ムーブコンストラクタは,セマンティクスに対するシンタックスがなかった.スマートポインタも. | C++11普及していないのはRHELが悪い | | RHEL 6のGCCのバージョンは4.4である。これは。C++11をまともにサポートしていない。次のRHELのバージョンは7であるが、これにはGCC 4.8が入るものとn思われる。しかし、すでにGCCの安定版は4.9だ。GCC 4.8もC++11実装に不具合が色々あってあまりお勧めできない | スタティックおじさん | | http://itpro.nikkeibp.co.jp/atcl/watcher/14/334361/122100450/ | mainの前に関数を呼ぶattribute constructorというものがある | | http://0xcc.net/blog/archives/000091.html | 例外起因のメモリリーク | | Rectangle* r(new Rectangle(p1, p2)); if (in_a_mess) throw Mess(); // 例外を投げてみる // ねぇ、例外が投げられたときの後始末は? delete r; | C++11を業務で使うことが許されない場合、Boostライブラリからスマートポインタが利用可能 | | | デストラクタを前提としたC++ではそもそも特別な仕組みを使わないと循環参照の解決は出来ない | | スマートポインタでたまに「循環参照が~」とか言って文句つける人が居ますが、冷静に考えてみてください、循環参照を発生させた時点でデストラクタの安全な実行が出来ません(片方を先に破棄すると、もう片方のオブジェクトはダングリングポインタを持つことになる)。 | g++ --verboseでIオプションの場所が全部出る | | |
疑問と後でまとめる †
- externについて.メソッドでexternをつけるのは慣習でしかない?変数だったらもちろん意味があるんだけど.
- ただデータ形式を変換するだけのクラス(実体がある必要が感じられないという意味)の実装方法ってどうすればいいんだろう?
- GoFデザインパターンとはクラスのメソッドを使用するにはそのクラスのインスタンスを生成しなければなりません。しかし、共通ライブラリのように使用頻度の高いメソッドの場合、そのつどインスタンスを生成していては効率が悪いのためstaticメソッドにすることが多い
- unitテストは全部のテストを統合した実行ファイルを作るべきなのか,一つ一つ実行ファイルを作るべきの?
- 例えばC++0xマスターが書いたプログラムは共同開発の時にエデュケーションコストを要求することになるが,それは組織の責任であるべきなのか.
例外(try, catch, throw)を学びたい †
http://wisdom.sakura.ne.jp/programming/cpp/cpp35.html
http://ufcpp.net/study/csharp/misc_exception.html
「メソッドの定める結果を達成できないなら例外を投げる」という方針でOK
利用法上の例外: 利用者側が正しい使い方をしていれば回避できる例外→例外はキャッチしない.例外の発生しうるメソッド本体(Doer: do するもの)に対して、 事前チェック用のテストメソッド(Tester)を用意する方法のことを Tester-Doer パターンやTryParse?パターンを使うと呼びます。これを使う
発生は避けれないが、復帰可能な例外→対処可能なら catch して対処してしまう。対処できない(あるいは、対処をもっと上の階層にゆだねた方がいい)場合catch しない。あるいは、一度 catch して、いくつか必要な操作(中途半端な状態を残さないためにロールバックを行ったり、ログを記録したり)を行ってから例外を再度投げる。標準の例外とは違う処理が必要な場合にのみ、自前で例外クラスを作成する(??).例えば、FileNotFoundException? を catch せずにそのまま上位に流してもいい場面では、自作の例外でラップしたりはしない。
以下の例面白い.ExistsからReadでファイルが消えている可能性を例外で潰す
try
{
if (!File.Exists(filename))
{
text = "デフォルトのテキスト";
}
else
{
text = File.ReadAllText(filename);
}
}
catch(FileNotFoundException?)
{
text = "ファイルがなくてもデフォルトのテキストがあれば OK";
}
対処のしようがない致命的な例外は,何もしない.
ラムダ †
- 文法
- [ キャプチャ ] ( パラメータ ) -> ret { 処理; }
- ラムダキャプチャ補遺
- =でキャプチャした変数はconstなので代入不能(mutableをパラメータ定義節のあとにつけるとconstが外れる)
// a,bを参照、それ以外はコピー
[=, &a, &b] () mutable { } ();
// a,bはコピー、それ以外は参照
[&, a, b] () mutable { } ();
- クラスメンバ関数内部のthisをキャプチャしたラムダは、内容を変更可能(thisがポインタなので)
[=]{this->i = 1;}();
その他 †
|