1

For example:

// No virtual methods in Base
class Base
{
public:
  Base() {}
  ~Base() {}
  void Foo();
};

// Derived class containing virtual methods -- will this cause problems??
class Derived : public Base
{
public:
  Derived() {}
  virtual ~Derived() {}
  virtual void Bar() {}
};

I've read that declaring at least one virtual (and/or pure virtual) function in a Base class will implicitly cause all Derived classes to apply the virtual keyword to those same (virtual) methods(s) defined in the Base. This makes sense.

Yesterday I read a comment to an answer where @Aaron-McDaid indicated:

... if you have a non-virtual Base class, and you have some virtual methods in your Derived class, then Base *b = new Derived(); delete b; will be undefined behaviour and possibly crash your program. It looks quite safe, but it's not. (It's because b won't point at the 'start' of the Derived object - it will be offset by the space needed for the vtable. Then, the delete will not be operating on exactly the same address as the new, and therefore it's not a valid address to free. If you are going to have virtual methods anywhere, then put a virtual d'tor in the base.

This sounds plausible to me -- does anyone know for certain whether it is accurate? If it is accurate, why doesn't C++ implicitly make a base method (e.g. destructor) virtual to keep the vtable at the front and prevent the undefined behavior?

Is there an example where one would not want this to happen?

EDIT: Is it safe to declare (and use) virtual methods in a derived class if the base class does not contain a virtual method?

Community
  • 1
  • 1
digitale
  • 645
  • 4
  • 13
  • 1
    "*Why doesn't C++ implicitly make a base method (e.g. destructor) virtual to keep the vtable at the front and prevent the undefined behavior?*" Because the C++ rule is that you don't pay for what you don't use. If you need a virtual destructor, declare one. The language has know way to know whether or not you're going to do `Base *b = new Derived(); delete b;` somewhere in your code. But you do, and you should know that if you do, you need a virtual destructor. – David Schwartz Apr 04 '16 at 04:05
  • Hi David, I updated my question to highlight the core question: Is it safe to declare (and use) virtual methods in a derived class when the base class has no virtual methods? – digitale Apr 04 '16 at 04:21
  • Short answer, yes. Perfectly safe. So long as you don't try to use the base class polymorphically. But you can use the derived class and classes derived from it polymorphically. – David Schwartz Apr 04 '16 at 07:47
  • Thanks to everyone who provided insight! All answers were excellent! – digitale Apr 05 '16 at 03:22

2 Answers2

3

Is it an error to declare a virtual method in a derived class whose base does not have a virtual method?

No, it's not an error. It is safe, as long as you don't delete the object using the wrong address, which can happen in the scenario Aaron discusses...

does anyone know for certain whether [Aaron-McDaid's assertions are] accurate?

Yes and no. It's true that " Base *b = new Derived(); delete b; will be undefined behaviour and possibly crash your program".

The explanation of exactly why is true on some systems, but the implementation mechanisms for virtual dispatch are unspecified by the Standard, which stipulates only behaviours that the compiler-writers have to orchestrate. There's no particular reason to think pointers to the virtual dispatch table will be at the front of objects. Still, you can write a program to see if it is on your own system:

#include <iostream>

struct Base
{
    int b_, b2_, b3_;
    Base() { std::cout << "Base(this " << (void*)this << ")\n"; }
};

struct Derived : Base
{
    int d_, d2_, d3_;
    Derived() { std::cout << "Derived(this " << (void*)this << ")\n"; }
    virtual ~Derived() { }
};

int main()
{
    Base* p = new Derived();
    std::cout << "p " << (void*)p << '\n';
}

With GCC/Linux, I'm seeing...

Base(this 0x12eda018)
Derived(this 0x12eda010)
p 0x12eda018

...which shows the Base subobject offset 8 bytes into Derived: that's less than the size of 3 ints, which implies it's something else: it's reasonable to assume a virtual dispatch pointer's in there as Aaron describes.

Separately, the "If you are going to have virtual methods anywhere, then put a virtual d'tor in the base." is necessary advice for the specific scenario the paragraph introduced with the ...; delete b; code, but shouldn't be mistaken as a necessity for all derived classes with virtual functions: only those that may be delete-ed through a Base*.

why doesn't C++ implicitly make a base method (e.g. destructor) virtual to keep the vtable at the front and prevent the undefined behavior?

Adding virtual functions to a class has consequences that may be undesirable, summarily:

  • additional memory use
  • slower performance
  • can restrict/prevent safe use of objects in shared memory from different processes
  • compromises encapsulation, as someone can avoid actions in your base-class destructor
  • may prevent memory layout compatibility with protocols or hardware

Base classes can be there for a great many reasons, including as "mix ins" to provide extra functionality, [template] "policies" controlling some functional aspect and possibly exposing some API for runtime control and/or querying, implementation support for the derived class etc.. In these situations, there's typically no need for a virtual destructor in the base class, and adding one indescriminately would bring the disadvantages above.

Community
  • 1
  • 1
Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
2

In case of non-virtual destructor, only destructor of base class will be called and if there are members of base classes are undefined/unset in derived class, that my lead to crash (although it is not the always the case).

The case mentioned in the quote is possible in case of multiple inheritance as there can be different offsets for the given base class. A inherited class is contained in the class as viewed from memory structure.

 _____________________________
| Base class 1                |
 _____________________________
| Base class 2                |
|    .......                  |
 -----------------------------

In the above case, if you try to use base class 2 pointer to delete or do anything, you might face the issue unless you take the offset from the object address. For Base class 1 pointer, you should not face any problem.

C++ does not make it implicit because inheritance might have different purposes. Moreover, virtual function has performance penalty. Sometimes, people does not need virtual destructor as base class is only used to take out something common and base class is not used to reference the objects (by the concept of OOPs, that may not be correct). C++ also supports private and protected inheritance unlike other languages. There using derived object as base class does not make any sense.

Example where one would not want to use virtual function is private inheritance where it means that derived class is implemented in terms of base class, not a base class. e.g. Stack can be implemented using array. Either, you can have an object of array in Stack or derive array privately.

class Stack: private Array {
  //Implement function using Array
}

Obviously, Stack is not an Array in this case anymore. Hence, there is no need to use virtual destructor in Array. Even if you use, you cannot free Stack because of private inheritance. There are many other cases possible.

In your case, you might not face a crash, but the members of derived class will not get free and gives you resource leaks.

doptimusprime
  • 9,115
  • 6
  • 52
  • 90