Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there some way to detect if a class is being incorrectly deleted using non-virtual base destructor?

Tags:

c++

The very well known scenario:

#include <memory>

class A{};
class B : public A {};

int main()
{
    std::unique_ptr<A> a = std::make_unique<B>();
    // bam, when a gets deleted, we have undefined behavior.
    return 0; 
}

Moreover, not even Valgrind can catch such an error, provided that the size of A and B are the same.

Is there some tool that could catch such errors at least for a debug build, or some idiom which would detect such errors for a specified class?

like image 728
Martin Drozdik Avatar asked Mar 27 '16 15:03

Martin Drozdik


2 Answers

For gcc you can specify:

-Wdelete-non-virtual-dtor -Wsystem-headers

to see delete-non-virtual-dtor warning generated by std::default_delete, it will look as follows:

/usr/local/include/c++/5.3.0/bits/unique_ptr.h:76:2: warning: deleting object of polymorphic class type 'B' which has non-virtual destructor might cause undefined behaviour [-Wdelete-non-virtual-dtor] delete __ptr;

live

btw. your sample classes lack at least one virtual function in base class.

[edit]

to turn it into error use:

 -Werror=delete-non-virtual-dtor -Wsystem-headers 
like image 73
marcinj Avatar answered Sep 22 '22 07:09

marcinj


It is my strong opinion that one should strictly separate "value-based" classes and "OO-based" classes (for lack of better term).

Value-based classes should have no public bases, and should normally support copy semantics (unless specially designed to disable copying). Objects of such classes have no identities apart from their values. They are interchangeable with their copies.

OO-based classes should have virtual destructors, and should never have copying members publicly accessible. Objects of such classes should only be copied via a virtual clone method. Such objects have identities separate from their values.

There should be no class with a public base that has a non-virtual destructor or a public copy/move ctor or a public copy/move assignment operator (in the base).

If you maintain this separation, you will have no accidents with object slicing or deletion through a non-polymorphic base pointer.

Unfortunately there are no tools (that I know of) that help maintain this separation. So you need to exercise due diligence while inheriting. It's really simple. Does it have a virtual dtor and inaccessible/deleted copy ctor and copy assignment? You can inherit publicly. No? Avoid potential mess, use composition or private inheritance.

A good class designed for inheritance would have its copying members protected in order to facilitate cloning in descendant classes.

Unfortunately, with third-party classes you don't have a choice, as authors usually leave copying members public, so there's still risk of object slicing. But you will have no risk of improper deletion through a base pointer.

TL;DR no tools, only programmer's due diligence.

like image 28
n. 1.8e9-where's-my-share m. Avatar answered Sep 24 '22 07:09

n. 1.8e9-where's-my-share m.