...
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <memory>
struct Base {
virtual void f();
};
struct Derived : Base {};
void f() {
std::unique_ptr<Base> b = std::make_unique<Derived()>unique<Derived>();
} |
Compliant Solution
In this compliant solution, the destructor for Base has an explicitly declared virtual destructor, ensuring that the polymorphic delete operation results in well-defined behavior.
| Code Block | ||||
|---|---|---|---|---|
| ||||
struct Base {
virtual ~Base() = default;
virtual void f();
};
struct Derived : Base {};
void f() {
Base *b = new Derived();
// ...
delete b;
} |
Exceptions
OOP52-CPP:EX0: Deleting a polymorphic object without a virtual destructor is permitted if the object is referenced by a pointer to its class, rather than via a pointer to a class it inherits from.
| Code Block | ||||
|---|---|---|---|---|
| ||||
class Base { public: // ... virtual void AddRef() = 0; virtual void Destroy() = 0; }; class Derived final : public Base { public: // ... virtual void AddRef() { /* ... */ } virtual void Destroy() { delete this; } private: ~Derived() {} }; |
Note that if Derived were not marked as final, then delete this could actually reference a subclass of Derived, violating this rule.
OOP52-CPP:EX1: Deleting a polymorphic object without a virtual destructor is permitted if its base class has a destroying operator delete that will figure out the correct derived class's destructor to call by other means.
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <new>
class Base {
const int whichDerived;
protected:
Base(int whichDerived) : whichDerived(whichDerived) {}
public:
Base() : Base(0) {}
void operator delete(Base *, std::destroying_delete_t);
};
struct Derived1 final : Base {
Derived1() : Base(1) {}
};
struct Derived2 final : Base {
Derived2() : Base(2) {}
};
void Base::operator delete(Base *b, std::destroying_delete_t) {
switch (b->whichDerived) {
case 0:
b->~Base();
break;
case 1:
static_cast<Derived1 *>(b)->~Derived1();
break;
case 2:
static_cast<Derived2 *>(b)->~Derived2();
}
::operator delete(b);
}
void f() {
Base *b = new Derived1();
// ...
delete b;
} |
Risk Assessment
Attempting to destruct a polymorphic object that does not have a virtual destructor declared results in undefined behavior. In practice, potential consequences include abnormal program termination and memory leaks.
Rule | Severity | Likelihood | Detectable |
|---|
Repairable | Priority | Level |
|---|---|---|
OOP52-CPP | Low | Likely |
No | No |
P3 |
L3 |
Automated Detection
Tool | Version | Checker | Description |
|---|
2116, 3403
| Astrée |
| non-virtual-public-destructor-in-non-final-class | Partially checked | ||||||
| Axivion Bauhaus Suite |
| CertC++-OOP52 | |||||||
| Clang |
| -Wdelete-non-virtual-dtor | |||||||
| CodeSonar |
| LANG.STRUCT.DNVD | delete with Non-Virtual Destructor | ||||||
| Helix QAC |
| C++3402, C++3403, C++3404 | |||||||
| Klocwork |
| CL.MLK.VIRTUAL |
| LDRA tool suite |
| 303 S | Partially implemented | ||||||
| Parasoft C/C++test |
|
|
| CERT_CPP- |
303 S
Partially implemented
OOP52-a | Define a virtual destructor in classes used as base classes which have virtual functions | ||||||||
| Polyspace Bug Finder |
| CERT C++: OOP52-CPP | Checks for situations when a class has virtual functions but not a virtual destructor (rule partially covered) | ||||||
| PVS-Studio |
| V599, V689 | |||||||
| RuleChecker |
| non-virtual-public-destructor-in-non-final-class | Partially checked |
| SonarQube C/C++ Plugin |
| S1235 |
Related Vulnerabilities
Search for other vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
| SEI CERT C++ Coding Standard | EXP51-CPP. Do not delete an array through a pointer of the incorrect type |
Bibliography
| [ISO/IEC 14882-2014] | Subclause 5.3.5, "Delete" |
| [Stroustrup 2006] | "Why Are Destructors Not Virtual by Default?" |
...
...