メインコンテンツ

AUTOSAR C++14 Rule A12-4-2

If a public destructor of a class is non-virtual, then the class should be declared final

説明

ルール定義

If a public destructor of a class is non-virtual, then the class should be declared final.

根拠

C++ では、派生クラスのオブジェクトが破棄されると、最初にそのクラスのデストラクターが呼び出されてから、基底クラスのデストラクターが呼び出されます。クラス階層はポリモーフィックにもなります。基底クラス ポインターを宣言して、それに派生クラス オブジェクトを割り当てることができます。クラス階層に属しているオブジェクトを安全に破棄するには、public クラス デストラクターを virtual として宣言します。派生オブジェクトを指している 2 つの基底クラス ポインターが破棄される次のコードについて考えます。

class Base{
public:
	virtual	~Base();
	//..
};

class Derived : public Base{
public:
	~Derived();
	//..
};
class Base2{
public:
	~Base2();
	//..
};

class Derived2 : public Base2{
public:
	~Derived2();
	//...
};
int main(){
	
	Base* ptr = new Derived;
	Base2* ptr2 = new Derived2;
	delete ptr;
	delete ptr2;
}

  • ptr オブジェクトは、クラス Derived のオブジェクトを指しているクラス Base のポインターです。ptr が削除されると、最初にその派生クラスのデストラクターが呼び出されてから、基底クラスのデストラクターが呼び出されます。ptr が基底クラス オブジェクトであっても、正しいデストラクターが呼び出されて取得されたすべてのリソースが解放されます。これは、このクラス階層内の public デストラクターが virtual として宣言されているためです。

  • ポインター ptr2 が削除されると、基底クラスのデストラクターのみが呼び出されます。これは、このクラス階層内の public デストラクターが非バーチャルであるためです。この種の不完全な破棄は未定義動作であり、メモリ リークや、コード実行の予期せぬ終了につながる可能性があります。

未定義動作を防止するために、public 非バーチャル デストラクターを含むクラスを基底クラスとして使用しないでください。このようなクラスを final として宣言し、これらのクラスが基底クラスではなく新しいクラスを派生できないことを指定してください。

Polyspace 実装

Polyspace® は、次の両方のステートメントが真の場合にクラス宣言にフラグを設定します。

  • クラスの public デストラクターが virtual として宣言されていない。

  • クラスが final として宣言されていない。

トラブルシューティング

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

すべて展開する

この例では、Polyspace がパブリック非バーチャル デストラクターを含む基底クラスにどのようにフラグを設定するかを示します。

#include<cstdint>
class Base{    //Noncompliant
public:
	~Base();
	//..
};

class Derived : public Base{  //Noncompliant
public:
	~Derived();
	//..
};
class Base2 final{  //Compliant
public:
	~Base2();
	//..
};

//class Derived2 : public Base2{ //Compilation error
//public:
//	~Derived2();
//	//...
//};
int main(){
	
	Base* ptr = new Derived;
	//	Base2* ptr2 = new Derived2;  //Compilation Error
	delete ptr;
	//	delete ptr2;
}

クラスの BaseDerived には、パブリック非バーチャル デストラクターが含まれています。main() では、ptr が破棄されると、~Base() のみが呼び出されるため、ポイントされているオブジェクトの部分破棄につながります。この動作はメモリ リークや予期せぬプログラム終了につながる可能性がある未定義動作です。Polyspace は、BaseDerived の両方の宣言にフラグを設定します。

クラス Base2 にはパブリック非バーチャル デストラクターが含まれています。Base2final として宣言されているため、このルールに準拠しています。Base2 からクラスを派生させると、コンパイル エラーになります。そのため、派生クラスのオブジェクトを指しているクラス Base2 のポインターを宣言できません。パブリック非バーチャル デストラクターを含むクラスを final として宣言することで、未定義動作が防止され、コードをメモリ リークや予期せぬプログラム終了から保護できます。

この例では、Polyspace が protected として宣言された非バーチャル デストラクターを許可する様子を示します。

#include<cstdint>
class Base{    //Compliant
protected:
	~Base();
	//..
};

class Derived : public Base{  //Compliant
protected:
	~Derived();
	//..
};

int main(){
	
	Base* ptr = new Derived;
	delete ptr;//Compilation error
}

protected として宣言された非バーチャル デストラクターはこのルールに準拠しています。Base のデストラクターは保護されているため、ステートメント delete ptr; はコンパイル エラーを引き起こします。非バーチャル デストラクターを protected として宣言すると、メモリ リークや予期せぬプログラム終了を防止できます。非 final クラスで非バーチャル デストラクターが protected として宣言されている場合は、クラスがこのルールに準拠しているため、Polyspace はそれらにフラグを設定しません。

チェック情報

グループ: 特殊なメンバー関数
カテゴリ: Advisory、Automated

バージョン履歴

R2020b で導入