CL.MLK.VIRTUAL

虚拟内存泄漏

这是一个类级别 (CL) 检查器,可告知用户可能发生的内存泄漏情况。如果相应类至少包含一个虚拟方法(意味着旨在作为基类进行继承),某个派生类的析构函数执行内存解除分配,但是基类析构函数未声明为虚拟,这时将报告 CL.MLK.VIRTUAL。在这种情况下,如果程序损坏了某个指针,且该指针指向某个派生类型对象的向上转换的基本类型,则将不会调用该派生类型的析构函数。

漏洞与风险

在这种情况下,与派生类型相关的动态内存不会得到正确销毁;也就是说,不会调用任何所需析构函数,因此这将导致意外的程序行为。

漏洞代码示例

复制
    #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 知识库进行扩展。有关详情,请参阅调整 C/C++ 分析。