Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this virtual destructor trigger an unresolved external?

Consider the following:

In X.h:

class X
{
    X();
    virtual ~X();
};

X.cpp:

#include "X.h"

X::X()
{}

Try to build this (I'm using a .dll target to avoid an error on the missing main, and I'm using Visual Studio 2010):

Error 1 error LNK2001: unresolved external symbol "private: virtual __thiscall X::~X(void)" (??1X@@EAE@XZ)

Small modifications result in a successful build, however:

X.h:

class X
{
    inline X(); // Now inlined, and everything builds
    virtual ~X();
};

or

X.h:

class X
{
    X();
    ~X(); // No longer virtual, and everything builds
};

What causes the unresolved external in the linker when the .dtor is virtual or when the .ctor isn't inlined?

EDIT:

Or, perhaps more interestingly, why do I not get an unresolved external if I make the destructor non-virtual, or if I inline the constructor?

like image 664
Greg D Avatar asked Aug 24 '10 20:08

Greg D


People also ask

How do you fix unresolved externals?

So when we try to assign it a value in the main function, the linker doesn't find the symbol and may result in an “unresolved external symbol” or “undefined reference”. The way to fix this error is to explicitly scope the variable using '::' outside the main before using it.

What does a virtual destructor do?

A virtual destructor is used to free up the memory space allocated by the derived class object or instance while deleting instances of the derived class using a base class pointer object.

What happens if destructor is not virtual?

Deleting a derived class object using a pointer of base class type that has a non-virtual destructor results in undefined behavior.

Why is destructor declared virtual?

Virtual keyword for destructor is necessary when you want different destructors should follow proper order while objects is being deleted through base class pointer.


2 Answers

Situation 1:

You have the code for the constructor.
So it builds the constructor into the object file. The constructor needs the address of the destructor to put into the virtual table because it can not find it the constructor can not be built.

Situation 2: (inline constructor)

The compiler decides it does not need to build the constructor (as it will be inlined).
As such it does not plant any code and therefore does not need the address of the destructor.

If you instanciate an object of type X it will again complain.

Situation 3: (non virtual destructor)

You don't need the address of the destructor to build the constructor.
So it does not complain.

It will complain if you instantiate an object of type X.

like image 184
Martin York Avatar answered Oct 12 '22 12:10

Martin York


You need to give a body to the virtual destructor:


class X
{
    X();
    virtual ~X() {}
};
like image 43
Dima Avatar answered Oct 12 '22 12:10

Dima