How to derive from a class without virtual-destructor? - c++

There is a virtual class as a callback interface, that I can neither modify, nor ask the author to fix. The only members of the class are a lot of virtual methods that can be overridden, so as to let the library call back into my code. In order to get some callback opportunities, I should make a derived class of that virtual class, and override corresponding virtual methods. If I'm NOT interested in some callback chances, I just need to avoid overriding them.
But, the declaration of that interface class has a defect - its destructor is NOT declared as virtual.
For example:
class callback_t {
public:
virtual void onData( int ) {};
};
I make a child class and do not override the destructor, but when I delete a dynamic object of the class child_t, I encounter a warning from the compiler (gcc9 with C++17) :
deleting object of polymorphic class type ‘child_t’ which has non-virtual destructor might cause undefined behavior.
class child_t : public callback_t {
public:
~child_t() {
// release things specific to the child...
};
void onData( int ) override {
// do things I want when onData
};
private:
int m_data = 0;
};
int main() {
child_t* pc = new child_t;
// pass pc into the routines of the library
// working...
delete pc; /*deleting object of polymorphic class type ‘child_t’ which has non-virtual destructor might cause undefined behavior */
};
Question:
How to correctly and gracefully eliminate the warnning(I must commit codes with no warnning)?
Notes and revising:
I can NOT modify the declaration of class callback_t, I also can NOT ask the author of it to fix! This is a lib that had been released by an authority institution. It's useless to advice me change the base class, and it's no meaning to judge the code quality of the lib;
I never intend to release objects of class child_t with a pointer of the type of the base class, and I known clearly the differenc between virtual-dtor and static-dtor;
The main problem I need to resolve, is to eliminate the compiling warnning, because I'm sure there's no memory leaking, and no omission of restoring some states;
In this case, there's no data, no meaningful codes in the base class, and the only intention of making it, is to be derived from, so it should NOT be marked final. But I tried making the child_t as 'final', and the warnning went away. I'm not sure this method is correct. If so, I think it is the cheapest methods so far;
I also tried making the dtor of child_t as virtual, and the warnning went away also. But I am still not sure whether or not it
is correct.

The warning you are getting is a false positive. With
child_t* pc = new child_t;
// pass pc into the routines of the library
// working...
delete pc;
pc points to a child_t, and it's static type is pointer to a child_t, so the correct destructor will be called. If you had
callback_t* pc = new child_t;
// pass pc into the routines of the library
// working...
delete pc;
Then the warning would be correct as only the callback_t destructor would be called.
There is a work around for this and that is to use a std::shared_ptr. The pointer stores the correct deleter in it's storage so even if the destructor is not virtual, the correct derived destructor is called instead of the base one. You can see more about this in shared_ptr magic :)

A virtual destructor is only needed if you need to delete derived objects via a base class pointer. If you don't need to do that, then the fact that the base class destructor is not virtual doesn't matter (although it's a fairly big hint that the class was never intended to be inherited from - in modern code it should probably have been marked final). You can still derive from the class just fine. You just have to be careful about how objects of the derived class are destroyed.

Related

Can a class hierarchy be safe and trivially copyable?

We use a framework that relies on memcpy in certain functions. To my understanding I can give everything that is trivially copyable into these functions.
Now we want to use a simple class hierarchy. We are not sure whether we can have a class hierarchy that results in trivially copyable types because of the safe destruction. The example code looks like this.
class Timestamp; //...
class Header
{
public:
uint8_t Version() const;
const Timestamp& StartTime();
// ... more simple setters and getters with error checking
private:
uint8_t m_Version;
Timestamp m_StartTime;
};
class CanData : public Header
{
public:
uint8_t Channel();
// ... more setters and getters with error checking
private:
uint8_t m_Channel;
};
The base class is used in several similar subclasses. Here I omitted all constructors and destructors. Thus the classes are trivially copyable. I suppose though that the user can write a code that results in a memory leak like this:
void f()
{
Header* h = new CanData();
delete h;
}
Is it right that the class hierarchy without the virtual destructor is a problem even if all classes use the compiler's default destructor? Is it therefore right that I cannot have a safe class hierarchy that is trivially copyable?
This code
Header* h = new CanData();
delete h;
will trigger undefined behavior since §5.3.5/p3 states:
In the first alternative (delete object), 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
and regardless of not having dynamically allocated objects contained in your derived class (really bad if you have), you shouldn't do it. Having a class hierarchy without the base class virtual destructor is not a problem per se, it becomes a problem when you try to mix static and dynamic types with delete.
Doing memcpy on a derived class object smells of bad design to me, I would rather address the need for a "virtual constructor" (i.e. a virtual clone() function in your base class) to duplicate your derived objects.
You can have your class hierarchy that is trivially copyable if you make sure that your object, its subobjects and base classes are trivially copyable. If you want to prevent users referring to your derived objects via base classes you could, as Mark first suggested, render the inheritance protected
class Header
{
public:
};
class CanData : protected Header
{ ^^^^^^^^^
public:
};
int main() {
Header *pt = new CanData(); // <- not allowed
delete pt;
}
Notice that you won't be able to use base pointers at all to refer to derived objects due to §4.10/p3 - pointer conversions.
If you delete a pointer to a derived type held as its base type and you don't have a virtual destructor, the derived types destructor won't be called, whether it's implicitly generated or not. And whether its implicitly generated or not, you want it to be called. If the derived type's destructor wouldn't actually do anything anyway though, it might not leak anything or cause a problem. If the derived type holds something like a std::string, std::vector, or anything with a dynamic allocation, you want the dtors to be called. As a matter of good practice, you always want a virtual destructor for base classes whether or not the derived classes destructors need to be called (since a base class shouldn't know about what derives from it, it shouldn't make an assumption like this).
If you copy a type like so:
Base* b1 = new Derived;
Base b2 = *b1;
You will only invoke Bases copy ctor. The parts of the object which are actually from Derived will not be involved. b2 will not secretly be a Derived, it will just be a Base.
My first instinct is "don't do that - find another way, a different framework, or fix the framework". But just for fun let's assume that for certain your class copy doesn't depend in any way on the copy constructor of the class or any of its comprised parts being called.
Then since you're clearly inheriting to implement rather than to substitute the solution is easy: Use protected inheritance and your problem is solved, because they can no longer polymorphically access or delete your object, preventing the undefined behavior.
It's almost safe. In particular, there is no memory leak in
Header* h = new CanData();
delete h;
delete h calls the destructor of Header and then frees the memory pointed to by h. The amount of memory freed is the same as was initially allocated at that memory address, not the sizeof(Header). Since Header and CanData are trivial, their destructors do nothing.
However, you must provide a virtual destructor to base even if it does nothing (by requirement of the standard to avoid undefined behaviour). A common guideline is that a destructor for a base class must be either public and virtual or protected and nonvirtual
Of course, you must beware slicing as usual.
Thanks all for posting various suggestions. I try a summarizing answer with an additional proposal for the solution.
The prerequisite of my question was to reach a class hierarchy that is trivially copyable. See http://en.cppreference.com/w/cpp/concept/TriviallyCopyable and especially the requirement of a trivial destructor (http://en.cppreference.com/w/cpp/language/destructor#Trivial_destructor). The class cannot need a destructor implemented. This restricts the allowed data members, but is fine for me. The example shows only C-compatible types without dynamic memory allocation.
Some pointed out that the problem of my code is undefined behaviour, not necessarily a memory leak. Marco quoted the standard regarding this. Thanks, really helpful.
From my understanding of the answers, possible solutions are the following. Please correct me if I am wrong. The solution's point is that the implementation of the base class must avoid that its destructor can be called.
Solution 1: The proposed solutions use protected inheritance.
class CanData : protected Header
{
...
};
It works but avoids that people can access the public interface of Header. This was the original intention to have a base class. CanData needs to forward these functions to Header. In the consequece, I would reconsider to use composition instead of inheritance here. But the solution should work.
Solution 2: Header's destructor must be protected, not the base class as a whole.
class Header
{
public:
uint8_t Version() const;
const Timestamp& StartTime();
// ... more simple setters and getters with error checking
protected:
~Header() = default;
private:
uint8_t m_Version;
Timestamp m_StartTime;
};
Then no user can delete Header. This is fine for me, because Header has no purpose on its own. With public derivation, the public interface remains available to the user.
My understanding is that CanData needs not implement a destructor to call the base class's desctructor. All can use the default destructor. I am not completely sure about this though.
All in all, the answers to my questions in the end of the origial positing are:
Is it right that the class hierarchy without the virtual destructor is a problem even if all classes use the compiler's default destructor?
It is only a problem if your destructor is public. You must avoid that people can access you desctrutor, except for derived classes. And you must ensure that derived classes call (implicitely) the base class's destructor.
Is it therefore right that I cannot have a safe class hierarchy that is trivially copyable?
You can make your base class safe with protected inheritance or a protected desctructor. Then you can have a hierarchy of trivially copyable classes.

C++ Non-Abstract Destructor Inheritance

I've seen this asked before, but not clearly or in the same situation as I have encountered.
I have an abstract base class. It has a protected constructor and a destructor. It is inherited by several complete types which also have public constructors and destructors. I'm having the issue where deleting the object isn't calling the child destructors if the object is referenced by the base type.
class Tree
{
protected:
Tree(){ }
public:
~Tree(){ }
};
class OakTree : public Tree
{
public:
OakTree(){ }
~OakTree(){ }
};
vector<Tree*> Trees; // Store objects using the base type
Trees.push_back(new OakTree()); // Create derived object
delete Trees[0]; // OakTree desctructor does not get called
How can I get the OakTree destructor called? I've tried marking all of the destructors to virtual but that didn't work. The base class destructor cannot be abstract (this would solve the calling problem but not the delete problem).
Make your base-class destructor virtual.
class Tree
{
protected:
Tree(){ }
public:
virtual ~Tree(){ }
}
Otherwise, undefined behavior will result if you try to delete through a base-class pointer. It's a bit dated, but Scott Meyers expressed this colorfully in Effective C++, 2nd ed:
The C++ language standard is unusually clear on this topic: when you try to delete a derived class object through a base class pointer and the base class has a nonvirtual destructor (as EnemyTarget does), the results are undefined. This means compilers may generate code to do whatever they like: reformat your disk, send suggestive mail to your boss, fax source code to your competitors, whatever. (What often happens at runtime is that the derived class's destrutcor is never called. ...)
It's because your base class destructor is not declared virtual.
class Tree
{
protected:
Tree(){ }
public:
virtual ~Tree(){ }
}
If you are going to use polymorphism you have to have a virtual destructor in your base class.
The problem is that when you call delete on a Tree * nobody really knows what kind of tree it is, and by having a virtual destructor the compiler generates a call through the pointer that points to the table for functions for that particular object instance, and you get the destructor for whatever that type might be.
Otherwise the standard says it is undefined behavior, but what seems to happen most of the times is the compiler generates a call to the destructor of whatever type your polymorphic pointer or reference is, in your case, that is the base class.
I wonder why nobody is actually asking what the author is trying to achieve with the code?
While, yes, having a virtual destructor in the base class will help you to get your destructor called, what you're still left with is undefined behavior.
What do you expect after the line delete Trees[0];?
Or, rather, what are you trying to achieve with that line?
If you're trying to erase the item from the vector, you might want:
Trees.erase(Trees.begin());
But then, you're left with the memory leak if you forget the delete line.
Hence, don't use plain pointers in your vector. Consider:
std::vector< std::shared_ptr<Tree> > Trees;
Trees.push_back( std::make_shared<OakTree>() );
Trees.erase(Trees.begin());
Runnable #Ideone

Why in destructors is the virtual table set back to that level?

Following this question - Pure virtual call in destructor of most derived class - I tried some code to check some syntax and discovered that as sucessive destructors are called, they call their relevant virtual functions. Consider this code:
class Base
{
public:
virtual void Method() = 0;
};
class Derived : public Base
{
public:
~Derived()
{
Method();
}
virtual void Method()
{
cout << "D";
}
};
class DoubleD : public Derived
{
public:
~DoubleD()
{
Method();
}
virtual void Method()
{
cout << "DD";
}
};
int main(array<System::String ^> ^args)
{
DoubleD D;
DoubleD E;
return 0;
}
As expected, as the object gets destructed, it calls the correct method (eg first the most derived and then the the second most derived).
Ouput: DD D
My question is, why does this work? Since you are not meant to call virtual functions in a c'tor/d'tor, why does the virtual table "unwind" correctly.
Eg, I can see why the most derived one works, that was the state the virtual function pointer table was in when this started. But why, when Derived's destructor is called, does the table get correctly set to point at that classes implementation of Method.
Why not just leave it, or if it is being nice, set the value to NULL.
Since you are not meant to call virtual functions in a c'tor/d'tor,
why does the virtual table "unwind" correctly.
The premise is wrong. There's nothing wrong with calling virtual functions from a constructor or destructor, provided you know how they work. As you've seen, the dynamic type is the type of the constructor or destructor being run, so you don't get virtual calls to the parts of the object that haven't yet been constructed or have already been destroyed.
The behaviour is perfectly well defined. You shouldn't worry about how your compiler vendor managed to implement it (though it's not very hard to reason out yourself, or just look up).
It's generally not advised to call virtual functions in the destructor because of the non-intuitive behaviour, but there's nothing fundamentally wrong with it.
This is how it is supposed to work according to the standard.
As for why, after you've run the destructor for a derived class you can't count on any of the properties of that class to be valid or consistent. Calling one of the virtual methods at that point would be a disaster if it went into a derived class method.
It's quite likely that the compiler bypasses the vtable altogether, since it already knows which overridden method applies to the current state of the object. That's just an implementation detail though.
Virtual table doesn't get modified at run time after the initial setup at object creation.
On some implementations, Virtual table shall be created as per class basis.
In your example, when DoubleD object is destroyed, it calls method function in DoubleD class, Because, the DoubleD part of the object is not yet destroyed completely.
VTable of DoubleD class has an entry for method function to point to method in its class as it is overridden(in the last level of inheritance)
Once DoubleD is destroyed, now the object type is of type Derived. So the call has to go to the method in vtable of class Derived. Hence the behavior.

Virtual destructors for interfaces

Do interfaces need a virtual destructor, or is the auto-generated one fine? For example, which of the following two code snippets is best, and why? Please note that these are the WHOLE class. There are no other methods, variables, etc. In Java-speak, this is an "interface".
class Base
{
public:
virtual void foo() = 0;
virtual ~Base() {}
};
OR...
class Base
{
public:
virtual void foo() = 0;
~Base() {} // This line can be omitted, but included for clarity.
};
EDIT DUE TO "NOT WHAT I'M LOOKING FOR" ANSWERS:
Exactly what are the consequences of each route. Please don't give vague answers like "it won't be destructed properly". Please tell me exactly what will happen. I'm a bit of an assembly nerd.
Edit 2:
I am well aware that the "virtual" tag means that the destructor won't get called if deleted through a pointer to derived, but (I think) this question ultimately boils down to "is it safe to omit that destructor, for is it truly trivial?"
EDIT 3:
My second edit is just plain wrong and disinformation. Please read the comments by actual smart people for more info.
Consider the following case:
Base *Var = new Derived();
delete Var;
You need the virtual destructor, otherwise when you delete Var, the derived class' destructor will never be called.
If you delete a derived class object via a base class pointer in C++, the result is undefined behaviour. UB is something you really want to avoid, so you must give base classes a virtual destructor. To quote from the C++ Standard, section 5.3.5:
if the static type of the operand is
different from its dynamic type, the
static type shall be a base class of
the operand’s dynamic type and the
static type shall have a virtual
destructor or the behavior is
undefined.
You should use a virtual destructor if you expect people to try to delete objects of a derived class via pointers or references of the parent class. If this is the case, then without a virtual destructor, the derived class will never be properly destructed.
For example,
Derived::~Derived() { // important stuff }
Base *foo = new Derived();
delete foo;
Without a virtual destructor in Base, Derived's destructor will never be called, and important stuff will therefore never happen.
Replying mostly to the edit:
Nobody can tell you what will happen because the result is "undefined behavior". When you delete a derived class through a pointer to a base that has no virtual destructor, the implementation is free to break down in any number of ways.
In general, a destructor should be either (1) public and virtual, or (2) protected and non-virtual.
Assuming you never expect anyone to delete a class instance via an interface pointer, a protected non-virtual destructor is 100% safe.
If someone tries to delete an interface pointer in case (2), they'll get a compile-time error.
No... virtual destructors are not auto generated. You have to declare them explicitely in your base class.
But you won't need to declare your destructors virtual for the child classes of Base. This is done by the compiler.
The compiler will also make sure that the destructors are called in reversed order of construction (from derived to base).
public class Base
{
//...
}
public class Derived
{
int i = 0;
//...
}
//...
Base* b = new Derived();
If you didn't have a virtual destructor
delete b;
would cause memory leaks (at least 4 bytes for the integer field), because it would destruct only Base and not Derived. The virtuality makes sure that the derived classes are destroyed, too. You won't have to declare a virtual constructor in Derived, this will be inferred by the compiler, if you declared a virtual destructor in Base.

Destructors for C++ Interface-like classes

Starting to use PC-Lint on an existing code base (fear and trepidation).
One thing that it complains about is the following:
class IBatch
{
public:
virtual void StartBatch() =0;
virtual int CommitBatch() =0;
};
Which when another class derives from this to use it like an interface
base class 'IBatch' has no destructor
So, the question: when you create Interface classes like the above, do you always include a virtual destructor? Why? (is it a style or a coding error?)
EDIT: Should have said that I do not expect or want the user of IBatch to destruct, they are a consumer of a service only, through this interface to some external implementing class (if that would make a difference)
A base class destructor should be either public and virtual, or protected and nonvirtual.
(Herb Sutter, Guru of the Week #18: "Virtuality")
Coding error - The destructor for your derived class will never get called if called via pointer to base class.
When you implement IBatch and you refer to your derived class by a pointer to a base class (pointer to IBatch) and you call delete on that pointer to base class you might end up with memory leak because the destructor for your derived class will never get called.
The basic rule is when a class has at least one virtual method it needs to have virtual destructor.
class IBatch
{
public:
virtual void f() = 0;
};
class A : public IBatch
{
public:
void f() {}
~A() {}
};
IBatch * a = new A();
a->f(); // calls A::f()
delete a; // calls IBatch::~IBatch() not A::~A()
If there are virtual functions, there needs to be a virtual destructor. Always. It does not matter that it's only an interface class -- it still needs the virtual destructor.
Either that, or it needs a protected nonvirtual destructor. But then you cannot delete the object using the interface pointer.
A class with virtual functions but no virtual destructor is suspect, and most likely wrong: see a good and more precise explanation here.
So, the question: when you create
Interface classes like the above, do
you always include a virtual
destructor? Why? (is it a style or a
coding error?)
Well it depends really. If you ever call delete on an IBatch pointer it probably won't do what you are expecting. Of course if you have something like virtual Init/Shutdowns or AddRef/Releases then its not really a problem.
Compiler puts default destructor that is not virtual, which implies that a 'delete' on a pointer to the virtual base class will succeed with a resulting memory leak. Therefore, it is an implementation flaw, neither style or coding error.