CodeQL 文档

从构造函数或析构函数进行虚拟调用

ID: cpp/virtual-call-in-constructor
Kind: problem
Security severity: 
Severity: warning
Precision: high
Tags:
   - reliability
   - readability
   - language-features
   - external/jsf
Query suites:
   - cpp-security-and-quality.qls

点击查看 CodeQL 代码库中的查询

此规则查找从构造函数或析构函数对虚拟函数的调用,这些调用可能会解析为与预期不同的函数。在实例化派生类时,虚拟函数调用的解析取决于定义*当前正在运行*的构造函数/析构函数的类型,而不是正在实例化的类。这是为了防止调用派生类中依赖于派生类中声明的字段的函数。在调用派生类的构造函数(*在*基类的构造函数*之后*)之前,此类字段的值未定义。同样,在调用基类的析构函数*之前*,将销毁派生类中声明的值。

指示的函数调用是对构造函数或析构函数中虚拟函数的调用,这很可能不会调用预期的函数,或者即使调用正确,如果没有类的继承图的知识也很难解释。

建议

不要从构造函数或析构函数调用虚拟函数。将基类中的虚拟函数更改为非虚拟函数,并从派生类传递任何必需的参数,或者在构造之后/销毁之前执行需要虚拟函数的初始化。

示例

class Base {
protected:
    Resource* resource;
public:
    virtual void init() {
        resource = createResource();
    }
    virtual void release() {
        freeResource(resource);
    }
};

class Derived: public Base {
    virtual void init() {
        resource = createResourceV2();
    }
    virtual void release() {
        freeResourceV2(resource);
    }
};

Base::Base() {
    this->init();
}
Base::~Base() {
    this->release();
}

int f() {
    // this will call Base::Base() and then Derived::Derived(), but this->init()
    // inBase::Base() will resolve to Base::init(), not Derived::init()
    // The reason for this is that when Base::Base is called, the object being
    // created is still of type Base (including the vtable)
    Derived* d = new Derived();
}

参考文献

  • ©GitHub 公司
  • 条款
  • 隐私