CL.MLK.VIRTUAL
仮想メモリ リーク
これはメモリリークの可能性をユーザーに通知するクラスレベル (CL) のチェッカーです。CL.MLK.VIRTUAL は、クラスが少なくとも 1 つの仮想メソッド (そのメソッドが継承では基本クラスとして動作することを意図する) を持ち、派生クラスのデストラクタがメモリ割り当て解除を行うが、その基本クラスのデストラクタが仮想と宣言されていない場合にレポートされます。この場合、プログラムは、派生タイプのオブジェクトへアップキャストする基底タイプを指すポインターを破壊しますが、派生タイプに対するデストラクタは呼び出されません。
脆弱性とリスク
そのような場合、派生タイプに関連付けられた動的メモリは正しく破壊されません。つまり、必要なデストラクタが呼び出されないため、予期しないプログラム動作を起こす結果になります。
脆弱コード例
コピー
#define TEXTURE_DATA char
#define TEXTURE_SIZE 512
class Shape {
public:
Shape() {}
~Shape() {}
void setArea(double a) { area = a; }
virtual void Draw();
private:
double area;
};
class Circle: public Shape {
public:
Circle() { texture = (TEXTURE_DATA*)malloc(TEXTURE_SIZE); }
~Circle() { free(texture); }
bool operator!=(Circle &r);
private:
TEXTURE_DATA *texture;
Circle(Circle &r) { texture = strdup(r.texture); }
Circle& operator=(Circle &r) { if (r != *this) texture = strdup(r.texture); return *this; }
};
void foo() {
Shape *newObj = new Circle;
// ... some code ...
delete newObj;
}
'texture' によって参照されるメモリは、デストラクタで割り当て解除されない可能性があります。基本クラス 'Shape' でデストラクタが仮想と定義されていないため、基本クラスのアップキャストによって削除するときに派生クラスのデストラクタは呼び出されず、その結果、メモリリークを引き起こします。
修正コード例
コピー
#define TEXTURE_DATA char
#define TEXTURE_SIZE 512
class Shape {
public:
Shape() {}
virtual ~Shape() {}
void setArea(double a) { area = a; }
virtual void Draw();
private:
double area;
};
class Circle: public Shape {
public:
Circle() { texture = (TEXTURE_DATA*)malloc(TEXTURE_SIZE); }
~Circle() { free(texture); }
bool operator!=(Circle &r);
private:
TEXTURE_DATA *texture;
Circle(Circle &r) { texture = strdup(r.texture); }
Circle& operator=(Circle &r) { if (r != *this) texture = strdup(r.texture); return *this; }
};
void foo() {
Shape *newObj = new Circle;
// ... some code ...
delete newObj;
}
修正したコードでは、7 行目に仮想デストラクタが配置されました。
関連チェッカー
外部参考資料
拡張機能
このチェッカーは、Klocwork knowledge base (ナレッジベース) を利用して拡張できます。詳細については、C/C++ 解析のチューニングを参照してください。