CERT.OOP.CTOR.VIRTUAL_FUNC

不可从构造函数或析构函数调用虚拟函数,但可从满足以下例外情况的构造函数或析构函数调用:

  • OOP50-CPP-EX1:允许调用具有显式限定 ID 的虚拟函数。

  • OOP50-CPP-EX2:允许从构造函数或析构函数调用具有最终虚拟说明符的虚拟函数。也允许从具有最终类虚拟说明符的类构造函数或析构函数调用虚拟函数。

漏洞与风险

应避免从构造函数或析构函数调用虚拟函数,原因如下:在构造过程中,派生类可能没机会初始化相关资源。同样地,尝试从析构函数调用派生程度更高的派生类中的函数,此操作可能会访问已释放的资源。

缓解与预防

请勿从构造函数或析构函数调用虚拟函数。

如需调用而不出问题,也可按照例外情况 OOP50-CPP-EX1 和 OOP50-CPP-EX2 中的指导操作。

漏洞代码示例

复制
     struct A {
      void seize_mine();
      void seize_fun();
      A()
      {
          seize();
          seize_mine();
      }
      virtual ~A()
     {
         release();
         seize_fun();
     }
   protected
     virtual void seize();
     virtual void release();
 };

 struct B : A{
   B():A() {      //OOP50-CPP-EX1: 允许调用具有显式限定 ID 的虚拟函数。
     B::seize();
     B::release();
   }
 };

 struct base {
   base();
   virtual void f();
 };

 struct derived : base {
   derived() : base() {     // OOP50-CPP-EX2: 允许从具有最终虚拟说明符的构造函数或析构函数调用虚拟函数。
     f();
   }
   void f() override final;
 };

 struct derived2 final : base {
   derived1() : base1() {     // OOP50-CPP-EX2: 如果类属于最终类,则允许从构造函数或析构函数调用虚拟函数。
     f();
   }
   void f() override;
 };

在此不符合要求的示例中,Klocwork 在第 6 和 11 行报告了 CERT.OOP.CTOR.VIRTUAL_FUNC 缺陷,因为从构造函数或析构函数调用虚拟函数可能会出现意想不到的结果,因此已禁用覆盖机制。允许调用具有显式限定 ID 的虚拟函数。也允许从具有最终类虚拟说明符的类构造函数或析构函数调用虚拟函数。

修正代码示例

复制
    struct A {
      void seize_mine();
      void seize_fun();
      A()
      {
          seize_mine();
      }
      virtual ~A()
      {
         seize_fun();
     }
   protected
     virtual void seize();
     virtual void release();
 };

 struct B : A{
   B():A() {   //OOP50-CPP-EX1: 允许调用具有显式限定 ID 的虚拟函数。
     B::seize();
     B::release();
   }
 };

 struct base {
   base();
   virtual void f();
 };

 struct derived : base {
   derived() : base() {   // OOP50-CPP-EX2: It is permissible to call a virtual function from a constructor or destructor that has the final virt-specifier
     f();
   }
   void f() override final;
 };

 struct derived2 final : base {
   derived1() : base1() {   // OOP50-CPP-EX2: 如果类属于最终类,则允许从构造函数或析构函数调用虚拟函数。
     f();
   }
   void f() override;
 };

以上示例符合要求,因为不会从构造函数或析构函数调用任何虚拟函数。