CERT.OOP.CTOR.VIRTUAL_FUNC
Virtual functions should not be called from constructors or destructors, but can be called from constructors or destructors with following exceptions:
-
OOP50-CPP-EX1: It is permissible to call a virtual function with an explicitly qualified ID.
-
OOP50-CPP-EX2: It is permissible to call a virtual function that has the final virt-specifier from a constructor or destructor. It is also permissible to call a virtual function from a constructor or destructor of a class that has the final class-virt-specifier.
Vulnerability and risk
Calling virtual functions from a constructor or destructor should be avoided for the following reasons: While under construction, derived classes may not have had the chance to initialize their resources. Similarly, attempting to call a function in a more derived class from a destructor can access resources that have already been released.
Mitigation and prevention
Do not call virtual functions from constructors or destructors.
You can also use the guidance in exceptions OOP50-CPP-EX1 and OOP50-CPP-EX2 to work around the issue.
Vulnerable code example
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: It is permissible to call the virtual function with an explicitly qualified 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: It is permissible to call a virtual function from a constructor or destructor if the class is final.
f();
}
void f() override;
};
In this noncompliant example, Klocwork reports a CERT.OOP.CTOR.VIRTUAL_FUNC defect on line 6 and line 11 because calling virtual functions in a constructor or destructor may provide unexpected results, because the overriding mechanism is disabled. It is permissible to call the virtual function with an explicitly qualified ID. It is also permissible to call a virtual function from a constructor or destructor of a class that has the final class-virt-specifier.
Fixed code example
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: It is permissible to call a virtual function with an explicitly qualified 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: It is permissible to call a virtual function from constructor or destructor if class is final.
f();
}
void f() override;
};
The above example is compliant because it does not invoke any virtual functions from a constructor or destructor.