Virtual destructors are useful when you might potentially delete an instance of a derived class through a pointer to base class:
class Base
{
// some virtual methods
};
class Derived : public Base
{
~Derived()
{
// Do some important cleanup
}
};
Here, you'll notice that I didn't declare Base's destructor to be virtual
. Now, let's have a look at the following snippet:
Base *b = new Derived();
// use b
delete b; // Here's the problem!
Since Base's destructor is not virtual
and b
is a Base*
pointing to a Derived
object, delete b
has undefined behaviour:
[In delete b
], if the static type of the
object to be deleted is different from its dynamic type, the static
type shall be a base class of the dynamic type of the object to be
deleted and the static type shall have a virtual destructor or the
behavior is undefined.
In most implementations, the call to the destructor will be resolved like any non-virtual code, meaning that the destructor of the base class will be called but not the one of the derived class, resulting in a resources leak.
To sum up, always make base classes' destructors virtual
when they're meant to be manipulated polymorphically.
If you want to prevent the deletion of an instance through a base class pointer, you can make the base class destructor protected and nonvirtual; by doing so, the compiler won't let you call delete
on a base class pointer.
You can learn more about virtuality and virtual base class destructor in this article from Herb Sutter.
Best Solution
Compiling a C++ program takes place in several steps, as specified by 2.2 (credits to Keith Thompson for the reference):
The specified errors occur during this last stage of compilation, most commonly referred to as linking. It basically means that you compiled a bunch of implementation files into object files or libraries and now you want to get them to work together.
Say you defined symbol
a
ina.cpp
. Now,b.cpp
declared that symbol and used it. Before linking, it simply assumes that that symbol was defined somewhere, but it doesn't yet care where. The linking phase is responsible for finding the symbol and correctly linking it tob.cpp
(well, actually to the object or library that uses it).If you're using Microsoft Visual Studio, you'll see that projects generate
.lib
files. These contain a table of exported symbols, and a table of imported symbols. The imported symbols are resolved against the libraries you link against, and the exported symbols are provided for the libraries that use that.lib
(if any).Similar mechanisms exist for other compilers/ platforms.
Common error messages are
error LNK2001
,error LNK1120
,error LNK2019
for Microsoft Visual Studio andundefined reference to
symbolName for GCC.The code:
will generate the following errors with GCC:
and similar errors with Microsoft Visual Studio:
Common causes include:
#pragma
(Microsoft Visual Studio)UNICODE
definitions