Deviatore
Deviatore

Reputation: 545

Linker refers to, supposedly, undefined reference to vtable

I am trying to use an abstract class to represent a common base for subtypes. However, it (the linker it seems) keeps moaning about vtables and undefined references no matter what I do. Judging by the error messages, the problem must be related to the destructors in some way. Wierdldy enough, it keeps talking about a

"undefined reference to 'AbstractBase::~AbstractBase()'"

in child.cpp which makes no sense.

Like last time, I can't actually show my code, so here is an example that in essence does the same thing:

First the abstract class, "AbstractBase.h":

#ifndef ABSTRACTBASE
#define ABSTRACTBASE

class AbstractBase
{
   public:
   virtual ~AbstractBase() = 0;
}
#endif

The child that uses the abstractbase, "child.h":

#ifndef CHILD
#define CHILD

class child : public AbstractBase
{
   public: 
      ~child() override;
}
#endif

The implementation in "child.cpp":

#include "child.h"
child::~child()

Obviously there are far more functions, but in essence that's how my real class's destructors look.

After scouring the web for ways of using abstract classes in C++, I am about to give up. As far as I can tell from those sources, this is the way to do it. You declare your abstracts class's destructor virtual, so any call to it will include the child. And the child's destructor is simply marked override. There shouldn't be anything else to it.

Have I missed something truly fundamental here?

PS: added MCVE:

class AbstractBase
{
   public:
   virtual ~AbstractBase() = 0;
};

class child : public AbstractBase
{
    public:
    void dostuff()
    {
      //stuff
    }

    ~child() override
    {}
}

int main (argc, char *argv[])
{
   child* ptr = new child();
   ptr->dostuff();
}

I should add that the errors I now get are not entirely identical, while the original ones look like this:

undefined reference to 'vtable for AbstractBase': In function AbstractBase:~AbstractBase()': Undefined reference to 'vtable for AbstractBase': Undefined reference to 'typeinfo for AbstractBase': Collect2:error:ld returned 1 exit status

Upvotes: 2

Views: 712

Answers (2)

Deviatore
Deviatore

Reputation: 545

I thank you all for your assistance. I eventually stumbled upon a solution.

Apperently, having regular virtual functions in the abstract class causes these issues. I recreated both the fix and the error in my MCVE, observe:

Nonfunctional code:

class AbstractBase
{
public:
    virtual void idiot();
    virtual ~AbstractBase() = 0;
};

AbstractBase::~AbstractBase()=default;

class child : public AbstractBase
{
    public:
    void dostuff()
    {
        //stuff
    }
    void idiot() override
    {

    }

    ~child() override
    {

    }
};

int main(int argc, char *argv[])
{
   child* ptr = new child();
   ptr->dostuff();
}

Functional code:

class AbstractBase
{
public:
    //virtual void idiot();
    virtual ~AbstractBase() = 0;
};

AbstractBase::~AbstractBase()=default;

class child : public AbstractBase
{
    public:
    void dostuff()
    {
        //stuff
    }

    /*void idiot() override
    {

    }*/

    ~child() override
    {

    }
};

int main(int argc, char *argv[])
{
   child* ptr = new child();
   ptr->dostuff();
}

Notice the only change I made, was commenting out the virtual function idiot, and it's implementation in child.

From my point of view, this is illogical. That extra function should not cause problems.

Alternatively, and this is the true solution, one can make all virtual functions pure. This solves the problem.

I can only guess at what's going on here, it would seem it looks for the implementation of the non-pure functions in a AbstractBase.cpp, which ofcourse doesn't exist. The result is the talk about undefined references to vtables and typeinfo for said AbstractBase, it is right in stating that the virtual functions are indeed undefined. But it shouldn't care, the class is abstract.

My conclusion would be, provided this is intended functionality, that you do need to declare all functions pure if you are to use abstract classes in c++, even though logic dictates it would be unnecessary. At any rate, if it is indeed intended, then the compiler should warn the user. The current errormessages are completely useless.

Upvotes: -1

Kerrek SB
Kerrek SB

Reputation: 477100

You need to define a destructor for every class, otherwise you cannot destroy objects of that class (which includes member objects and base sub-objects):

class AbstractBase
{
public:
   virtual ~AbstractBase() = default;
}; //                     ^^^^^^^^^^^

Some alternative formulations:

  • User-defined:

    struct AbstractBase {
        virtual ~AbstractBase() {}
    };
    
  • Pure-virtual, but defined:

    struct AbstractBase {
        virtual ~AbstractBase() = 0;
    };
    
    AbstractBase::~AbstractBase() = default;
    

    This has the benefit of leaving the class abstract even if you have no other virtual member functions.

  • Combine the two:

    struct AbstractBase {
        virtual ~AbstractBase() = 0;
    };
    
    AbstractBase::~AbstractBase() {}
    

Upvotes: 6

Related Questions