メインコンテンツ

AUTOSAR C++14 Rule A15-2-1

Constructors that are not noexcept shall not be invoked before program startup

説明

ルール定義

Constructors that are not noexcept shall not be invoked before program startup.

根拠

C++ では、コンパイラが次の手順に従って例外に対処します。

  • コンパイラは、現在のスコープまたは上位スコープ内のハンドラーと例外を一致させようとします。

  • 例外がハンドラーと一致すると、そのハンドラーが例外を受け入れ、スタックのアンワインドを開始します。スタックのアンワインド中は、例外を発生させるスコープから、外側のスコープへと、逆の順序でプログラム実行が移動します。次にプログラム実行は、まだ破棄されていないスタック上の変数ごとにデストラクターを呼び出します。スタックのアンワインド後は、トリガーされたハンドラーの直後の行からプログラム実行が再開します。

  • 例外がハンドラーと一致しなかった場合は、コンパイラが処理系定義に従って実行を終了させます。つまり、プログラム終了の具体的なプロセスは、使用しているソフトウェアとハードウェアの特定の組み合わせに応じて異なります。たとえば、コンパイラが std::terminate() を呼び出すと、std::abort() が呼び出され、実行が異常終了される場合があります。実装に基づいて、プログラムが中止される前にスタックがアンワインドされない可能性があります。プログラムが終了する前にスタックがアンワインドされなかった場合は、スタック内の変数のデストラクターが呼び出されず、リソース リークやセキュリティの脆弱性につながります。

プログラムの起動前に、静的オブジェクトまたはグローバル オブジェクトのコンストラクターが呼び出され、オブジェクトを構築して初期化します。このようなコンストラクターが例外を発生させた場合は、コンパイラがスタックをアンワインドせずにコードの実行を異常終了させる可能性があります。静的オブジェクト obj のコンストラクターが例外を引き起こす可能性がある次のコードについて考えます。

class A{
	A(){
		
		//...
	}	
};

static A obj;

main(){
	//...
}
main() が開始する前に、静的オブジェクト objA() を呼び出すことにより構築されます。A() はプログラムの起動前に呼び出されるため、どの例外ハンドラーも A() によって発生した例外と一致しません。実装に基づいて、このような例外はスタックをアンワインドせずにプログラムを終了させる可能性があり、メモリ リークやセキュリティの脆弱性につながります。

静的オブジェクトまたはグローバル オブジェクトのコンストラクターによって発生された例外は例外ハンドラーと一致しないため、このようなコンストラクターは noexcept と宣言します。

Polyspace 実装

Polyspace® は、静的オブジェクトまたはグローバル オブジェクトの非 noexcept コンストラクターが直接呼び出されるステートメントにフラグを設定します。また、非準拠のコンストラクターを強調表示します。

トラブルシューティング

ルール違反が想定されるものの、Polyspace から報告されない場合は、コーディング規約違反が想定どおりに表示されない理由の診断を参照してください。

すべて展開する

この例では、Polyspace が静的オブジェクトまたはグローバル オブジェクトのコンストラクターにどのようにフラグを設定するかを示します。

#include <cstdint>
#include <stdexcept>
#include <string>
class A
{
public:
	A() noexcept : x(0){}
	A(std::int32_t n) : x(n) {
		throw std::runtime_error("Unexpected error"); 
	}
	A(std::int32_t i, std::int32_t j) noexcept : x(i + j)
	{
		try {
			throw std::runtime_error("Error");
		}
		catch (std::exception& e) {
		}
	}
private:
	std::int32_t x;
};

static A a1;     // Compliant
static A a2(5);  // Noncompliant
static A a6(5);  // Noncompliant
static A a3(5, 10); // Compliant
A a4(5);  //Noncompliant          
A a5(5, 10); //Compliant
int foo_A(A a) { };

int bar_A(int value) {
	A a{value};   //Compliant
	return foo_A(a);
}
int value2b = bar_A(20);   // Compliant
std::string s{"Hello World"};//Noncompliant
int value2a = foo_A(20);   //Noncompliant
int convert(){

	
	return  foo_A(a2);   
}

  • Polyspace はステートメント std::string s{"Hello World"}; にフラグを設定します。これは、このステートメントが、プログラムの起動前に文字列 s の非 noexcept コンストラクターを呼び出しているためです。

  • Polyspace は、a2a4a6 の構成にフラグを設定します。これらの構成は、プログラムの起動前に非 noexcept コンストラクター A(std::int32_t n) を呼び出しているためです。

  • Polyspace は、a5 の構成にはフラグを設定しません。この構成は noexcept コンストラクター A(std::int32_t i, std::int32_t j) を呼び出しているためです。

  • Polyspace はステートメント int value2a = foo_A(20); にフラグを設定します。これは、int から A に暗黙的に変換するには、起動前に非 noexcept コンストラクター A(std::int32_t n) を呼び出す必要があるためです。

  • Polyspace は関数 bar_A() の本体内のステートメント A a{value}; にフラグを設定しません。これは、オブジェクト a がローカルであり、プログラムの起動中に作成されないためです。

チェック情報

グループ: Exception Handling
カテゴリ: Required、Automated

バージョン履歴

すべて展開する