要約しましょう。過去 2 年間で、私は C++ コア ガイドラインに約 100 件の投稿を書きました。なんで?ドキュメントの回答:「このドキュメントは、C++ を適切に使用するための一連のガイドラインです。このドキュメントの目的は、人々が最新の C++ を効果的に使用できるようにすることです。」. しかし、私の話はここで終わりではありません。ガイドラインには補足セクションがあります。
100記事って結構多いですよね。ガイドラインのサポート セクションに入る前に、C++ コア ガイドラインへの私の既存の投稿を見つける手助けをしたいと思います。
<オール>サポート セクションの概要は次のとおりです。
- A:アーキテクチャのアイデア
- NR:非規則と神話
- RF:参考文献
- プロ:プロフィール
- GSL:ガイドライン サポート ライブラリ
- NL:命名規則とレイアウト規則
- FAQ:よくある質問への回答
- 付録 A:ライブラリ
- 付録 B:コードの最新化
- 付録 C:考察
- 付録 D:サポート ツール
- 用語集
- To-do:未分類のプロトルール
中身を見てみましょう。
A:アーキテクチャのアイデア
最初のセクションはかなり短いです。ルールは 3 つしかなく、内容はわずか 3 文です。彼らの焦点は、プログラミング言語にとらわれないことです。
A.1:安定性の低いコードから安定したコードを分離
ルールの文は次のとおりです。「安定性の低いコードを分離することで、単体テスト、インターフェースの改善、リファクタリング、および最終的な廃止が促進されます。」 わかりました、それはどういう意味ですか?
安定したコードと不安定なコードの間にインターフェースを配置することは、それを分離する方法です。インターフェイスにより、安定性の低いコードは一種のサブシステムになり、分離してテストまたはリファクタリングできます。サブシステムをテストするだけでなく、アプリケーションへのサブシステムの統合もテストできるようになりました。最初の種類のテストは通常、単体テストと呼ばれ、2 番目のサブシステム統合テストは呼ばれます。サブシステムには、機能チャネルと非機能チャネルの 2 つのアプリへのチャネルがあります。どちらもテストする必要があります。機能チャネルはサブシステムの機能を提供し、非機能チャネルは発生する可能性があり、アプリケーションが反応する可能性のある例外を提供します。インターフェイスのおかげで、具体的なサブシステムはインターフェイスの実装であり、したがって、別のより安定した実装にすぐに置き換えることができます。
A.2:潜在的に再利用可能なパーツをライブラリとして表現する
わかりました、これはとても簡単ですが、この点に関して、より難しい質問 2 の答えがあります。
<オール>3 つの質問は非常に曖昧であり、したがって、答えるのが困難です。これは、特に最後の質問に当てはまります。試してみましょう。
まず第一に、「あなたはそれを必要としない」(YAGNI)という理由でライブラリとして再利用できるようにするためにコードに多大な労力を費やすのではなく、再利用できるようにコードを書いてください。これは、将来、あなたや他のプログラマーがコードを操作しなければならない可能性が非常に高いため、理解可能性、保守性、テスト容易性、およびその他の能力のためにコードを作成するなどの簡単なガイドラインに従うことを意味します。あるいは、Philip Wadler の言葉を借りれば、「コードを読めるようにしましょう。あなたのコードを次に見る人はサイコパスであり、彼はあなたがどこに住んでいるかを知っているふりをしてください. "
同じまたは類似の機能がもう一度必要な場合は、「自分自身を繰り返さないでください」(DRY)。ここで、抽象化について最新のことを考える必要があります。 2 つの同様の関数がある場合、実装を表す 3 番目の関数を作成します。同様の関数は、実装関数を使用するための単なるラッパーです。これが私の考えをコードにまとめたものです。
std::vector<void*> myAlloc; void* newImpl(std::size_t sz,char const* file, int line){ // (3) static int counter{}; void* ptr= std::malloc(sz); std::cerr << file << ": " << line << " " << ptr << std::endl; myAlloc.push_back(ptr); return ptr; } void* operator new(std::size_t sz,char const* file, int line){ // (1) return newImpl(sz,file,line); } void* operator new [](std::size_t sz,char const* file, int line){ // (2) return newImpl(sz,file,line); }
単純な形式 (1 行目) と配列 (2 行目) のオーバーロードされた new 演算子は、(3 行目) で実装を呼び出します。
答えは多くの要因に依存するため、質問 3 には答えたくありません。これは、ソフトウェアのドメインによって異なります。たとえば、ソフトウェアはデスクトップ、組み込みデバイス、または高取引サーバーで実行されますか。保守性、テスト容易性、スケーラビリティなどの要因に依存しますが、パフォーマンスにも依存します。ユーザーのスキルレベルによって異なります。おそらく、あなたのライブラリはインフラストラクチャ ライブラリまたは顧客向けのライブラリです。
ライブラリの形で再利用可能なソフトウェアを作成することは、一方向のシュートを行うよりも約 3 ~ 4 倍の労力がかかります。私の経験則は次のとおりです。機能を再利用することがわかっている場合は、ライブラリについて検討する必要があります。機能を少なくとも 2 回再利用する場合は、ライブラリを作成する必要があります .
A.4:ライブラリ間に循環があってはなりません
ライブラリ間のサイクルは、ソフトウェア システムをより複雑にします。まず、ライブラリのテストが難しくなりますが、個別に再利用することはできなくなります。第 2 に、ライブラリの理解、維持、および拡張がより困難になります。そのような依存関係を見つけたら、それを壊すべきです。 John Lakos (Large Scale C++ Software Design, p185) によるいくつかの可能性があります:
<オール>次は?
非規則と神話の次の補足セクションには、より多くの内容があります。ルール以外のほとんどは神話としてすでに知っていると思います。次回の投稿で、それらの謎を解き明かしましょう。