Getting started
Literals
Basic type keywords
Operator precedence
Floating point arithmetic
Bit operators
Bit manipulation
Bit fields
Arrays
Flow control
const keyword
Loops
mutable keyword
friend keyword
keywords
Variable declaration keywords
auto keyword
Pointers
Type keywords (class, enum, struct, union)
Classes / structs
std::string
Enumeration
std::atomic<T>
std::vector
std::array
std::pair
std::map
std::unordered_map
std::set and std::multiset
std::any
std::variant
std::optional
std::integer_sequence
std::function
std::forward_list
std::iomanip
Iterators
Basic I/O
File I/O
Streams
Stream manipulators
Metaprogramming
Returning multiple values from a function
References
Polymorphism
Value and reference semantics
Function call by value vs. by reference
Copying vs assignment
Pointers to class / struct members
The this pointer
Smart pointers
Unions
Templates
Namespaces
Function overloading
Operator overloading
Lambdas
Threading
Value categories
Preprocessor
SFINAE
Rule of three, five and zero
RAII
Exceptions
Implementation-defined behavior
Special member functions
Random numbers
Sorting
Regular expressions
Perfect forwarding
Virtual member functions
Undefined behavior
Overload resolution
Move semantics
Pimpl idiom
Copy elision
Fold expressions
Unnamed types
Singleton
ISO C++ Standard
User-defined literals
Type erasure
Memory management
Explicit type conversions
RTTI
Standard library algorithms
Expression templates
Scopes
Atomic types
static assert
constexpr
Date and time with std::chrono
Trailing return type
Function template overloading
Common compile linker errors
Design patterns
Optimizations
Compiling and building
Type traits
One definition rule
Unspecified behavio
Argument dependent name lookup
Attributes
Internationalization
Profiling
Return type covariance
Non-static member functions
Recursion
Callable objects
Constant class member functions
C++ vs. C++ 11 vs C++ 17
Inline functions
Client server examples
Header files
Const correctness
Refactoring techniques
Parameter packs
Iteration
type deduction
C++ 11 memory model
Build systems
Concurrency with OpenMP
Type inference
Resource management
Storage class specifiers
Alignment
Inline variables
Linkage specifications
Curiusly recurring template pattern
Using declaration
Typedef and type aliases
Layout of object types
C incompatibilities
Optimization
Semaphore
Thread synchronization
Debugging
Futures and promises
More undefined behaviors
Mutexes
Recursive mutex
Unit testing
decltype
Digit separators
C++ Containers
Arithmetic meta-programming
Contributors

Virtual and Protected Destructors

suggest change

A class designed to be inherited-from is called a Base class. Care should be taken with the special member functions of such a class.

A class designed to be used polymorphically at run-time (through a pointer to the base class) should declare the destructor virtual. This allows the derived parts of the object to be properly destroyed, even when the object is destroyed through a pointer to the base class.

class Base {
public:
    virtual ~Base() = default;

private:
    //    data members etc.
};

class Derived : public Base { //  models Is-A relationship
public:
    //    some methods

private:
    //    more data members
};

//    virtual destructor in Base ensures that derived destructors
//    are also called when the object is destroyed
std::unique_ptr<Base> base = std::make_unique<Derived>();
base = nullptr;  //    safe, doesn't leak Derived's members

If the class does not need to be polymorphic, but still needs to allow its interface to be inherited, use a non-virtual protected destructor.

class NonPolymorphicBase {
public:
    //    some methods

protected:
    ~NonPolymorphicBase() = default; //    note: non-virtual

private:
    //    etc.
};

Such a class can never be destroyed through a pointer, avoiding silent leaks due to slicing.

This technique especially applies to classes designed to be private base classes. Such a class might be used to encapsulate some common implementation details, while providing virtual methods as customisation points. This kind of class should never be used polymorphically, and a protected destructor helps to document this requirement directly in the code.

Finally, some classes may require that they are never used as a base class. In this case, the class can be marked final. A normal non-virtual public destructor is fine in this case.

class FinalClass final {  //    marked final here
public:
    ~FinalClass() = default;

private:
    //    etc.
};

Feedback about page:

Feedback:
Optional: your email if you want me to get back to you:



Table Of Contents