C++

プログラミング~ C

概要

  • C++とSTLの細かい書き方
  • 未定義
    • O0で動いてO3で死ぬ -> 未定義を踏んでいる
    • O3で動いてO0で死ぬ -> 未定義を踏んでいる

下位ページ

目次

C/C++の発達段階

  • C言語にはいくつかのレベルがある
    • valgrind, テスト,ドキュメント,その他を全て考慮した作業スタイルというか手順総体を学ぶ教育はあるのか?
段階許される開発習得すべき技術
入出力がきちんと動いているかも確認しない
ソースコードコピペをしない再利用性が死んでると,ファイルごとコピーみたくなって,メンテナンス不能なので,マジそれだけはやめて
入出力がきちんと動くことを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^mlong 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({0, 0})簡単なvectorの作り方

STL

vector

strstream

  • ss.str()は終端文字std::endsを自動挿入しない
   int code = 1000;
   std::strstream ss;
   ss << code << std::ends;

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;
    }
};

メモリ管理

  • std::unique_ptrは所有権を唯一(unique)持っているように振る舞うスマートポインタです。
    • つまり、複数のstd::unique_ptrのインスタンスが同一のメモリ領域を管理することはありません。
    • std::shared_ptrと同様に変数の寿命が来た時に、自動でメモリ領域を解放してくれます。

isnanがあいまい

  • std::isnanとすれば解決

absは危険

  • absを使いたいときはstd::absとする.
    • 無名空間のabsの返り値はlongで非常に危険.

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 } };は,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 としても良い
静的変数・メソッドはアクセス制限のあるグローバル静的メンバ変数はオブジェクトに属するメンバ変数ではなく、クラスに属するメンバ変数です。アクセス制限が設定されていることを除けば、グローバル変数と同じ
epollC++の通信をコンカレントに行うことができるシステム
  • コーディングルール
知識名サンプル備考
vectorは使ってはならないhttp://d.hatena.ne.jp/aki-yam/20120307/1331117485
hppもcppもnamespaceで囲うcppはusing namespace Namespace;でもできてしまうが,非常に不自然な上,明示的でないので,きちんと囲うこと.
C++ではtypedefの代わりにusingを使う可読性が高く,テンプレートをtypedefできる.http://notmoon.hateblo.jp/entry/2015/05/13/151939 (リンク切れ) http://cpplover.blogspot.jp/2009/05/ctypedefaliasdeclaration.html http://qiita.com/Linda_pp/items/44a67c64c14cba00eef1
C++はキャスト演算子を使ってのセマンティクスを明示すべきCのキャストは危険なのとセマンティクスが多義的なので良くない.static_castはいつものキャスト.const_castはconstとvalatileを除去するキャスト.reinterpret_castは基本非推奨.void*, int*への変換でWinAPI32とかで使う.dynamic_castは基本非推奨(継承関係をチェック。キャストできればキャストしてキャスト後の値, キャストできなければfalse.この処理にはポリモフィズムを使用すべき.従来のC風のキャストを使用したコードを安全に置き換える用途以外には使用すべきではありません) http://www.s34.co.jp/cpptechdoc/article/newcast/ (リンク切れ) http://d.hatena.ne.jp/tmatsuu/20090717/1247835994 http://www.geocities.jp/bleis_tift/cpp/newcast.html (リンク切れ)
C++では,Cのポインタのセマンティクスがシンタックスに対しことて多義的なので,スマートポインタ群を使うべき安全性と開発速度の向上が見込めるだけでなく、プログラマの意図に合わせて「ポインタ」を記述し分けることができる.基本的には全部shared pointerで,外に出すときはweakにして責任がないことを明示し,クラス内やメソッド内の限られたスコープの場合はuniqueにする.uniqueだったものを外に出したくなる場合は当然ある.そういう時にはuniqueのポインタをsharedに書き換える.速度は?スマートポインタのいいサンプル集 リンク 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のエラーコードを返して死なないライブラリは絶対勝手に死んではならない
継承する時、親と子で同じ変数を持ってはいけない。そもそも親のメンバ変数にアクセスするには、Super::val = 3;
テンプレート子クラスは、親のメンバ変数にアクセスするときには、必ずSuper::val = 3;
コンストラクタ・デストラクタでpure virtual関数の実装を呼んじゃダメ!!pure virtual method called普通に