ソースコードなるべく最初から使える設計にしたいものです。
そのような設計をするために個人的に思っていることをメモします。
いったんチーム内にリリースしていまうと、なかなかディレクトリ構成でさえ、変えにくくなったりすることがあります。ですから、最初から問題のないディレクトリ構成で、問題のない関数・メソッドのインターフェースでリリースすることです。最初から使える設計にする指針があると考えます。それをこの記事で紹介します。
(今までに、このサイトの記事に書いていることとダブっているかと思います。)
ディレクトリ構成
バージョン管理しているディレクトリの中に出力データを書き込まない
ソースコード・入力データのサンプルなどはバージョン管理の対象ですし、必要な一式をまとめて圧縮して配布したりします。
そのときに、実行結果の出力が、それらのディレクトリのツリー構造の中にあると、必要な一式をまとめて圧縮して配布する前に、出力データを取り除く作業が必要になってしまいます。
出力データディレクトリは入力データディレクトリと別のディレクトリにする。
理由:出力ディレクトリはバージョン管理の必要がない。
clean するときに、出力データが特定の出力ディレクトリに集中していれば、clean しやすい。
入力データは時として大容量で、バージョン管理の対象外になる。
入力データディレクトリには、入力データ以外のものを書き込まない。そうすれば、入力データディレクトリをまとめて圧縮して管理すればよくなる。圧縮したものを、ネットワークの共有保管場所におけばよくなる。
出力データディレクトリはスクリプトのディレクトリ、ソースコードディレクトリとも別のディレクトリにする。
バージョン管理するディレクトリに出力データを書き込まないようにする。そうすることで、スクリプトディレクトリやソースコードディレクトリに余分なものが入ることを防げる。
そうすることでバージョン管理が楽になる。
関数・メソッドの引数
画像の入力、文字列の入力、独自型の入力には全てconst 属性をつける。
入力の引数には全てconst 属性をつける。
インスタンスを引数渡しするときは、参照渡しする。
参照渡しすることで、不要なオブジェクトのコピーを生成することを予防する。
参照渡しにすることで、ポインタ渡しを使わない。このことでポインタにまつわる様々なやっかいごとを予防する。
ループ
std::vectorの各要素にアクセスする際には、for 文の上限を .size()メソッドをfor文の外で一回呼べば十分なようにする。
以下は推奨しないコード
auto a=std::vector<cv::Point3d>;
何かデータを追加する処理;
for (size_t i=0 ; i <a.size() ; i++){
// a[i] にアクセスする処理
std::cout << a[i] << std::endl;
}
auto a=std::vector<cv::Point3d>;
何かデータを追加する処理;
size_t n = a.size();
for (size_t i=0 ; i < n ; i++){
// a[i] にアクセスする処理
std::cout << a[i] << std::endl;
}
std::forward_list, std::listの各要素に全てアクセスする際には、イテレータを使う。
listの添え字でアクセスすると、a[i] をa[0]から毎回リスト構造を最初から順にたどっていくので、その分の処理で遅くなってしまう。
イテレータを使うことでその分が回避される。
C++ 単方向リストクラス std::forward_list 入門
事前に大きさが分かっているstd::vector型は最初から領域を確保する
reserve() を使う。
以上のように、予めわかっている設計指針を実装してからリリースするのが得策だと思っています。
付記:
以上、分かっている人にとっては自明なことだとは思いますが、そうでない人のために書いてみました。
class の実装をするときには、1つのクラスになんでもかんでもぶちこまない。1つの意味あいが明確な範囲でクラスに実装する。
参考記事: C++高速化