インターフェイスは、サービス プロバイダーとサービス コンシューマーの間の契約です。 C++ コア ガイドラインには、それらを正しくするための 20 のルールがあります。これは、「インターフェイスは、おそらくコード編成の最も重要な単一の側面である」ためです。
前回の投稿で、最初の 10 のルールについて書きました。今日は仕事を終えて、残りの 10 のルールについて書きます。
- I.1:インターフェースを明示的にする
- I.2:グローバル変数を避ける
- I.3:シングルトンを避ける
- I.4:インターフェースを正確かつ強く型付けする
- I.5:状態の前提条件 (ある場合)
- I.6:
Expects()
を優先 前提条件を表現するため - I.7:状態事後条件
- I.8:
Ensures()
を優先 事後条件の表現用 - I.9:インターフェースがテンプレートの場合、概念を使用してそのパラメーターを文書化します
- I.10:例外を使用して、必要なタスクの実行に失敗したことを知らせる
- I.11:生のポインターによって所有権を譲渡しない (
T*
) - I.12:null であってはならないポインターを
not_null
として宣言する - I.13:配列を単一のポインタとして渡さない
- I.22:グローバル オブジェクトの複雑な初期化を避ける
- I.23:関数の引数の数を少なく保つ
- I.24:同じ型の関連性のないパラメータを隣接させない
- I.25:クラス階層へのインターフェースとして抽象クラスを好む
- I.26:クロスコンパイラ ABI が必要な場合は、C スタイルのサブセットを使用してください
- I.27:安定したライブラリ ABI については、Pimpl イディオムを検討してください
- I.30:ルール違反をカプセル化する
詳細に直接飛び込みましょう。
I.11:raw ポインターによって所有権を譲渡しない (T*)
このコードには概念上の問題があります。
X* compute(args) // don't { X* res = new X{}; // ... return res; }
ポインター X を削除するのは誰ですか?所有権の問題に対処するには、少なくとも 3 つの代替手段があります。
- 可能であれば値を返す
- スマート ポインターを使用する
- ガイドライン サポート ライブラリ (GSL) の所有者
を使用
I.12:必要なポインターを宣言するnot_null として null ではない
次の関数の長さの 3 つのバリエーションの意味上の違いは何ですか?
int length(const char* p); // it is not clear whether length(nullptr) is valid int length(not_null<const char*> p); // better: we can assume that p cannot be nullptr int length(const char* p); // we must assume that p can be nullptr
長さのバリエーション 2 と 3 の意図は明らかです。 2 番目のバリエーションは null 以外のポインターのみを受け入れ、3 番目のバージョンは nullptr を受け入れます。あなたはすでにそれを推測しているかもしれません。 GSL からの場合は not_null。
I.13:配列を渡さない単一のポインタとして
配列を単一のポインターとして渡すと、エラーが発生しやすくなります。
void copy_n(const T* p, T* q, int n); // copy from [p:p+n) to [q:q+n)
n が大きすぎるとどうなりますか?右:未定義の動作。 GSL は、スパンと呼ばれるソリューションを提供します。
void copy(span<const T> r, span<T> r2); // copy r to r2
スパンは引数の数を推測します。
I.22:グローバル オブジェクトの複雑な初期化を避ける
グローバル オブジェクトは、多くの楽しみを提供します。たとえば、それらが異なる翻訳単位にある場合、それらの初期化の順序は定義されていません。次のコード スニペットの動作は未定義です。
// file1.c extern const X x; const Y y = f(x); // read x; write y // file2.c extern const Y y; const X x = g(y); // read y; write x
I.23:関数の引数の数を少なく保つ強い>
単純なルールがあります。1 つの関数が 1 つのジョブを正確に実行する必要があります。その場合、関数の引数の数は自動的に少なくなり、関数は使いやすくなります。
正直なところ、標準テンプレート ライブラリの新しい並列アルゴリズム (std::transform_reduce など) は、このルールを破ることがよくあります。
I.24:同じタイプの隣接する無関係なパラメーターを避けるタイプ
次の copy_n 関数のコピー元とコピー先は?根拠のある推測はありますか?
void copy_n(T* p, T* q, int n);
しばしばドキュメントを探す必要があります。
I.25:クラスへのインターフェースとして抽象クラスを優先する階層
もちろん、これはオブジェクト指向設計の明確で確立された規則です。ガイドラインは、この規則の 2 つの理由を示しています。
- 抽象クラスは基本クラスよりも安定している可能性が高い
- 状態メソッドと非抽象メソッドを持つ基本クラスは、派生クラスにより多くの制約を課します
I.26:もしクロスコンパイラ ABI が必要な場合は、C スタイルのサブセットを使用してください
ABI は für A を表します アプリケーション B inary 私
これは、C++ ガイドラインでは奇妙な規則です。その理由は、「異なるコンパイラは、クラス、例外処理、関数名、およびその他の実装の詳細に対して異なるバイナリ レイアウトを実装している」ためです。一部のプラットフォームでは、一般的な ABI が出現しています。単一のコンパイラを使用する場合は、完全な C++ インターフェイスに固執できます。この場合、コードを再コンパイルする必要があります。
I.27:安定したライブラリ ABI については、にきびイディオム
Pimpl は実装へのポインターを表し、ブリッジ パターンの C++ バリエーションです。非ポリモーフィック インターフェイスはその実装へのポインターを保持するため、実装を変更してもインターフェイスを再コンパイルする必要がないという考え方です。
以下は、C++ コア ガイドラインの例です:
interface (widget.h) class widget { class impl; std::unique_ptr<impl> pimpl; public: void draw(); // public API that will be forwarded to the implementation widget(int); // defined in the implementation file ~widget(); // defined in the implementation file, where impl is a complete type widget(widget&&) = default; widget(const widget&) = delete; widget& operator=(widget&&); // defined in the implementation file widget& operator=(const widget&) = delete; }; implementation (widget.cpp) class widget::impl { int n; // private data public: void draw(const widget& w) { /* ... */ } impl(int n) : n(n) {} }; void widget::draw() { pimpl->draw(*this); } widget::widget(int n) : pimpl{std::make_unique<impl>(n)} {} widget::~widget() = default; widget& widget::operator=(widget&&) = default;
pimpl は、実装へのハンドルを保持するポインターです。
この C++ イディオムの詳細については、Herb Sutter による GOTW #100 記事を参照してください。 GotW は Guro of the Week の略です。
I.30:ルール違反をカプセル化する
さまざまな理由により、コードが醜い、安全でない、またはエラーが発生しやすい場合があります。コードを 1 か所に配置し、使いやすいインターフェイスでカプセル化します。それは抽象化と呼ばれ、時々やらなければならないことです。正直なところ、使用されている内部コードが安定していて、インターフェースが正しい方法でしか使用できない場合は、そのコードに問題はありません.
次は?
今回の投稿を含め、前回の投稿で、ガイドライン サポート ライブラリについてよく言及しました。では、インサイトを見てみましょう。それについては次の投稿で書きます。