4

Consider the following three programs in C++:

program 1

struct base{
  virtual ~base() =0;
};

struct derived: public base{
  ~derived();
};

derived::~derived(){}

int main(){}

program 2

struct base{
  virtual ~base() =0;
};

struct derived: public base{
  ~derived(){}
};

int main(){}

program 3

struct base{
  virtual void func() =0;
};

struct derived: public base{
  void func();
};

void derived::func(){}

int main(){}

The programs #2 and #3 compile and run fine however the first gives the following error:

Undefined symbols for architecture x86_64:
  "base::~base()", referenced from:
    derived::~derived() in main-d923b9.o
ls: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

I'd like to know why I am unable to define virtual destructors outside of the class definition however I am able to do it inside the class definition. Additionally I can define methods outside the class, just not destructors.

YSC
  • 38,212
  • 9
  • 96
  • 149
Mathew
  • 1,116
  • 5
  • 27
  • 59
  • 3
    This doesn't look like JavaScript to me ? The C++ tag would be better suited to this question – Muhammad Salman May 14 '18 at 16:31
  • Sorry about that, I forgot to change it from the previous question I asked. Also thanks for the catch on the constructor vs destructor. the question is still valid however – Mathew May 14 '18 at 16:34
  • 1
    None of these code samples actually compile (or provide the linker error you posted). Please provide real [mcve]s, don't invent code on the fly – UnholySheep May 14 '18 at 16:36
  • 1
    `virtual ~base() =0;` pure virtual `virtual ~base() {}` virtual `virtual ~base() =0 {};` pure virtual with impl – UKMonkey May 14 '18 at 16:37
  • 2
    A pure virtual destructor must be defined and it can be done only outside of the class definition due to syntax issues. – Constructor May 14 '18 at 16:39
  • all the code should either compile/run now or give the error. If it doesn't let me know and I'll look into it – Mathew May 14 '18 at 16:44
  • Possible duplicate of [Pure virtual destructor in C++](https://stackoverflow.com/questions/630950/pure-virtual-destructor-in-c) – hyde May 14 '18 at 16:46

2 Answers2

4

This is wrong

struct base{
  virtual ~base() =0;
};

because base::~base is not defined. Even though it has been declared as a pure virtual, it needs to be defined (outside of the class, there is no syntaxic way to declare a function as pure virtual and define it inline) so derived can inherit from it:

struct base
{
    virtual ~base() = 0;
};

base::~base() {}

I'd like to know why I am unable to define virtual destructors outside of the class definition

Well, you can: I just did.


Then, why do you need to implement a function (~base) which has been declared pure virtual?

Since derived inherit from base, when an object of type derived is destructed, it necessarily calls base's destructor. This is how inheritance work. In a sense, derived::~derived needs to link with base::~base. Even though base::~base is a pure virtual (meaning a class inheriting from base is not a complete type unless it defines a destructor) it needs to be defined so ~derived finds it and the linker become happy.

YSC
  • 38,212
  • 9
  • 96
  • 149
  • There is nothing wrong with pure virtual destructor declaration. Your wording is wrong. – Maxim Egorushkin May 14 '18 at 16:47
  • ok, I added base::~base(){} to the code and it works. I'm confused though because I thought that you didn't need to implement virtual functions and it seems like your implementing it when you do this – Mathew May 14 '18 at 16:48
  • @MaximEgorushkin care to expand on that? I don't get it. – YSC May 14 '18 at 16:49
  • to phrase my question differently, why is base::~base(){} not needed in the second 2 programs – Mathew May 14 '18 at 16:49
  • then why does it compile and run fine in the second 2 cases? – Mathew May 14 '18 at 16:50
  • It's needed in all the programs. Sounds like a compiler bug if program 2 built without error. – Stephen M. Webb May 14 '18 at 16:51
  • are you sure that its a bug? I just ran program 2 on mac and on linux, neither gave an error – Mathew May 14 '18 at 16:55
  • @fred added explanation – YSC May 14 '18 at 16:55
  • @fred: the difference between a regular method and a destructor is that at the end of a derived class destructor it's as if there's an implicit call to the base class destructor, so if that isn't defined you'll get a linker error. – Matteo Italia May 14 '18 at 16:57
  • ok that makes sense. Thanks a lot for all the help, I'm still concerned however that the second program is running and compiling on both my mac and linux machines – Mathew May 14 '18 at 16:58
  • @fred it doesn't really come in as a surprise, the compiler just sees a class declaration that nobody uses, that translation unit doesn't produce anything that references any of those destructors. The first example instead emits the destructor for `derived`, which implicitly references the destructor of `base`. – Matteo Italia May 14 '18 at 17:43
1

The answer to why program 2 builds lays in how link works. When function definition is outside the class declaration, linker needs to create a body of the function in the final binary file so it needs to have ~base() definition as well.

If you put in case 2 derived d; in main body, you'll get the same error as in case 1 as the linker will have to create the derived member functions definitions and will need ~base() body as in case 1.

Spock77
  • 3,256
  • 2
  • 30
  • 39