C++ Core Guidelines:Lifetime Safety And Checking the Rules

C++ コア ガイドラインの有効期間の安全性プロファイルは、1 つの問題に要約されます。無効な可能性のあるポインターを逆参照しないでください。初期化されていないか、または nullptr. であるため、ポインターが無効である可能性があります ポインターは、その怒りを超えて、または削除されたオブジェクトを指す場合もあります。

生涯安全

可能性のある無効なポインターを逆参照しない場合、プログラムへの影響は、C++ コア ガイドライン マニホールドに従っています:
  • C++ の厄介なエラーの主な原因の 1 つを排除します
  • 潜在的なセキュリティ違反の主な原因を排除
  • 冗長な「パラノイア」チェックを排除することでパフォーマンスを向上
  • コードの正確性に対する信頼を高める
  • 主要な C++ 言語ルールを適用することにより、未定義の動作を回避します

正直なところ、ポインターを扱うことは、所有権という大きな話の一部です。所有権とは、各時点で、誰がオブジェクトの存続期間を管理する責任があるかを明確にする必要があることを意味します。大まかに言えば、C++11 は 6 種類の所有権をサポートしています:

  • ローカル オブジェクト .所有者がこれらのリソースの有効期間を自動的に管理するための C++ ランタイム。同じことが、グローバル オブジェクトまたはクラスのメンバーにも当てはまります。ガイドラインでは、スコープ オブジェクトと呼んでいます。
  • 参考文献 :私は所有者ではありません。空にできないリソースだけを借りました。
  • 生のポインタ :私は所有者ではありません。空にできるリソースだけを借りました。リソースを削除してはなりません。
  • std::unique_ptr :私はリソースの独占所有者です。リソースを明示的に解放する場合があります。
  • std::shared_ptr :リソースを他の共有ポインタと共有します。共有所有権を明示的に解放することができます。
  • std::weak_ptr :私はリソースの所有者ではありませんが、std::weak_ptr::lock メソッドを使用して一時的にリソースの共有所有者になることができます。

このきめの細かい所有権セマンティックを生のポインターと比較してください。さて、最近の C++ の好きなところはわかりました。

ここで、次のように自問することもできます:ルールを持つことは問題ありませんが、自分のコードがこれらのルールに従っていることを確認するにはどうすればよいでしょうか?ガイドライン サポート ライブラリ (GSL) のおかげで、C++ コア ガイドラインのルールを自動的にチェックできます。

ガイドラインのルールの確認

GSL は、C++ コア ガイドラインのガイドラインをサポートするための小さなライブラリです。それらは、利用可能な GSL のいくつかの実装です。

GSL はヘッダーのみのライブラリであるため、ライブラリの関数と型を非常に簡単に使用できます。最もよく知られている実装は、Github でホストされている Microsoft の実装です:Microsoft/GSL. Microsoft バージョンは C++14 のサポートを必要とし、さまざまなプラットフォームで動作します。いくつかの非常に人気のあるプラットフォームを次に示します:

  • Visual Studio 2015 を使用する Windows
  • Visual Studio 2017 を使用する Windows
  • Visual Studio 2019 を使用する Windows
  • Clang/LLVM 3.6 を使用する Windows
  • Clang/LLVM 7.0.0 を使用する Windows
  • GCC 5.1 を使用する Windows
  • インテル C++ コンパイラー 18.0 を使用する Windows
  • Clang/LLVM 3.6-3.9 を使用する GNU/Linux
  • Clang/LLVM 4.0 を使用した GNU/Linux
  • Clang/LLVM 5.0 を使用した GNU/Linux
  • Clang/LLVM 6.0 を使用した GNU/Linux
  • Clang/LLVM 7.0 を使用した GNU/Linux
  • GCC 5.1 を使用する GNU/Linux

GSL で何ができるか見てみましょう。 Type Safety、Bounds Safe、および Lifetime Safety を破るプログラムを次に示します。

型安全性、境界安全性、生涯安全性の破れ

// gslCheck.cpp

#include <iostream>

void f(int* p, int count) {
}

void f2(int* p) {
 int x = *p;
}

int main() {

 // Break of type safety
 // use of a c-cast
 double d = 2;
 auto p = (long*)&d;
 auto q = (long long*)&d;

 // Break of bounds safety
 // array to pointer decay
 int myArray[100];
 f(myArray, 100);

 // Break of Lifetime Safety
 // a is not valid
 int* a = new int;
 delete a;
 f2(a);

}

ソースコード内のコメントは、私の問題を文書化しています。それでは、Visual Studio 2019 を起動して、問題を視覚化する手順を示します。

ビルド時にコード分析を有効にする

チェックボックスを有効にする必要があります。デフォルトでは、タイプ セーフティ、バウンド セーフティ、ライフタイム セーフティの 3 つのルールは、Microsoft ネイティブ推奨ルールには含まれていません。

アクティブ ルールを構成する

スクリーンショットからわかるように、C++ コア ガイドライン境界ルール、C++ コア ガイドライン タイプ ルール、および C++ コア ガイドライン ライフタイム ルールで構成されるルールセット CheckProfile を作成します。

ソリューションでコード分析を実行

私の一連のルールをコード例に適用することは、非常に有望でした.

すべての問題が見つかりました。最初の問題などの問題ごとに、行番号 (17) と、影響を受けるプロファイルのルール (type.4) を取得します。

警告を抑制する

特定の警告を抑制したい場合があります。これは、属性を使用して実現できます。次の例では、ポインターの減衰に配列を 2 回適用します。 2 番目の呼び出しのみが警告を発します。

// gslCheckSuppress.cpp

#include <iostream>

void f(int* p, int count) {
}

int main() {

 int myArray[100];
 
 // Break of bounds safety
 [[gsl::suppress(bounds.3)]] { // suppress warning
 f(myArray, 100);
 }

 f(myArray, 100); // warning 

}

属性 gsl::suppress(bounds.3) は期待どおりに動作します。その範囲内でのみ有効です。境界安全の 2 番目のブレークが表示されます。

次は?

私はすでにガイドライン サポート ライブラリに投稿したので、C++ コア ガイドラインの次のセクションはスキップします。次の章はかなり物議を醸すものになると思います:命名とレイアウトの規則.