Any way to detect the absense of virtual destructor in base class?

Q

Qi

The full question:
Any portable and standard way to detect the absence of virtual
destructor in base class?

Boost has some type traits for that, but they are intrinsic and rely
on the compilers internal implementation.

Here is some sample code,

class A {
// no virtual destructor
};

class B : public A {
// some data here, whatever
};

A * a = new B;
delete a;

If B has virtual destructor, the behavior of "delete a" is UB. (I
spent half an hour on this issue).
If B has no virtual dtor, memory leak!

So the ideal way is, whenever newing a B like that, a static assert
failure or runtime failure is thrown to indicate A needs a virtual dtor.

Is it possible?
I doubt that, but it's quite annoying to debug that problem in case
I forget to give A a virtual dtor.

If it's impossible, any suggestion on how to avoid forgetting give
base class a virtual dtor?


Thanks
 
A

Alf P. Steinbach

The full question:
Any portable and standard way to detect the absence of virtual
destructor in base class?

Boost has some type traits for that, but they are intrinsic and rely
on the compilers internal implementation.

Here is some sample code,

class A {
// no virtual destructor
};

class B : public A {
// some data here, whatever
};

A * a = new B;
delete a;

If B has virtual destructor, the behavior of "delete a" is UB. (I
spent half an hour on this issue).
If B has no virtual dtor, memory leak!

So the ideal way is, whenever newing a B like that, a static assert
failure or runtime failure is thrown to indicate A needs a virtual dtor.

Is it possible?
I doubt that, but it's quite annoying to debug that problem in case
I forget to give A a virtual dtor.

If it's impossible, any suggestion on how to avoid forgetting give
base class a virtual dtor?

Why are you using a raw pointer?

Use `std::unique_ptr` or `std::shared_ptr`, or e.g. `boost::shared_ptr`.

These smart pointers remember the proper derived class destruction to
use, freeing you having to have a virtual destructor.


Cheers & hth.,

- Alf
 
V

Vladimir Jovic

The full question:
Any portable and standard way to detect the absence of virtual
destructor in base class?

Boost has some type traits for that, but they are intrinsic and rely
on the compilers internal implementation.

Here is some sample code,

class A {
// no virtual destructor
};

class B : public A {
// some data here, whatever
};

A * a = new B;
delete a;

If B has virtual destructor, the behavior of "delete a" is UB. (I
spent half an hour on this issue).
If B has no virtual dtor, memory leak!

So the ideal way is, whenever newing a B like that, a static assert
failure or runtime failure is thrown to indicate A needs a virtual dtor.

Is it possible?
I doubt that, but it's quite annoying to debug that problem in case
I forget to give A a virtual dtor.

If it's impossible, any suggestion on how to avoid forgetting give
base class a virtual dtor?

That check would be more complex then adding the "virtual" keyword to
the destructor of the base class.

Anyway, here you are:

#include <boost/type_traits/has_virtual_destructor.hpp>
struct a
{
virtual ~a(){}
};
struct b
{
virtual ~b(){}
};

// ok
static_assert( boost::has_virtual_destructor<a>::value, "no virt
destructor" );
// error
static_assert( boost::has_virtual_destructor<b>::value, "no virt
destructor" );

int main()
{
}
 
S

SG

The full question:
Any portable and standard way to detect the absence of virtual
destructor in base class?

Since you said "standard way" and did not explicitly exclude C++2011:

#include <type_traits>

:::

std::has_virtual_destructor<YourType>::value

Cheers!
SG
 
Q

Qi

Since you said "standard way" and did not explicitly exclude C++2011:

#include<type_traits>

:::

std::has_virtual_destructor<YourType>::value

Thanks

This, plus static assert, seems an elegant solution.

But,
1, Any equivalence in C++2003 ?
2, If not, any "standard" way to detect the compiler
supports only 03 or also supports 11? So I can have some
backward compatibility.
 
Q

Qi

Why are you using a raw pointer?

Use `std::unique_ptr` or `std::shared_ptr`, or e.g. `boost::shared_ptr`.

These smart pointers remember the proper derived class destruction to
use, freeing you having to have a virtual destructor.

I'm a fan of scoped pointer (though not fan of shared pointer).
But raw pointer is still quite useful to me.
 
Q

Qi

// ok
static_assert( boost::has_virtual_destructor<a>::value, "no virt
destructor" );
// error
static_assert( boost::has_virtual_destructor<b>::value, "no virt
destructor" );

That Boost trait has some assumption on the compiler dependent
object binary layout, AFAIK.
So it's neither standard nor portable.
 
M

Marc

Qi said:
2, If not, any "standard" way to detect the compiler
supports only 03 or also supports 11? So I can have some
backward compatibility.

The value of the macro __cplusplus *should* do it.
 
L

Lee Steven

The full question:
Any portable and standard way to detect the absence of virtual
destructor in base class?

Boost has some type traits for that, but they are intrinsic and rely
on the compilers internal implementation.

Here is some sample code,

class A {
// no virtual destructor

};

class B : public A {
// some data here, whatever

};

A * a = new B;
delete a;

If B has virtual destructor, the behavior of "delete a" is UB. (I
spent half an hour on this issue).
If B has no virtual dtor, memory leak!

So the ideal way is, whenever newing a B like that, a static assert
failure or runtime failure is thrown to indicate A needs a virtual dtor.

Is it possible?
I doubt that, but it's quite annoying to debug that problem in case
I forget to give A a virtual dtor.

If it's impossible, any suggestion on how to avoid forgetting give
base class a virtual dtor?

Thanks

Here, I think If A has no virtual dtor, memory leak!
 
J

Juha Nieminen

Alf P. Steinbach said:
Use `std::unique_ptr` or `std::shared_ptr`, or e.g. `boost::shared_ptr`.

These smart pointers remember the proper derived class destruction to
use, freeing you having to have a virtual destructor.

How exactly do they achieve that? If I do, for example, this:

std::unique_ptr<Base*> ptr = new Derived;

then how exactly is 'ptr' able to deduce the derived class destructor in
order to directly call it when it disposes of the object?

(And where would it store it anyways? I thought the whole idea with
std::unique_ptr is that its size is that of one pointer, hence making
it as efficient as a raw pointer.)

I wonder if you are being confused by shared_ptr (and possible other
smart pointers) being able to destroy an object even if they only see
a class declaration (rather than a definition), as long as the class was
fully declared at the point of construction of the pointer. (IOW the class
doesn't need to be fully declared at the place of destruction.) That's not
the same thing as a base-class type smart pointer being able to deduce
the derived-class type destructor.
 
A

Alf P. Steinbach

How exactly do they achieve that? If I do, for example, this:

std::unique_ptr<Base*> ptr = new Derived;

then how exactly is 'ptr' able to deduce the derived class destructor in
order to directly call it when it disposes of the object?

(And where would it store it anyways? I thought the whole idea with
std::unique_ptr is that its size is that of one pointer, hence making
it as efficient as a raw pointer.)

I wonder if you are being confused by shared_ptr (and possible other
smart pointers) being able to destroy an object even if they only see
a class declaration (rather than a definition), as long as the class was
fully declared at the point of construction of the pointer. (IOW the class
doesn't need to be fully declared at the place of destruction.) That's not
the same thing as a base-class type smart pointer being able to deduce
the derived-class type destructor.

I'm looking at N3290, which I believe is identical to the final standard.

And there std::unique_ptr stores a deleter, which can be function object
or a function pointer or whatever, specified in constructor call.

Essentially it's the same idea as for shared_ptr's deleter except that
that unique_ptr doesn't use templating to infer the real type of the
object pointer it gets, so one has to write like

unique_ptr<Base> p = unique_ptr<Derived>( new Derived );

The only trouble is, I can't get the deleter functionality to work with
either Visual C++ 10.0 or g++ 4.4.1 in Windows, nor with g++ 4.6.1 in
Ubuntu.

That might indicate that my understanding is wrong (in which case the
deleter argument then seems worthless), or that unique_ptr is not fully
implemented by these compilers, or that N3290 is not identical to C++11?


Cheers, & thanks for focusing me on that,

- Alf
 
A

Alf P. Steinbach

I'm looking at N3290, which I believe is identical to the final standard.

And there std::unique_ptr stores a deleter, which can be function object
or a function pointer or whatever, specified in constructor call.

Essentially it's the same idea as for shared_ptr's deleter except that
that unique_ptr doesn't use templating to infer the real type of the
object pointer it gets, so one has to write like

unique_ptr<Base> p = unique_ptr<Derived>( new Derived );

The only trouble is, I can't get the deleter functionality to work with
either Visual C++ 10.0 or g++ 4.4.1 in Windows, nor with g++ 4.6.1 in
Ubuntu.

That might indicate that my understanding is wrong (in which case the
deleter argument then seems worthless), or that unique_ptr is not fully
implemented by these compilers, or that N3290 is not identical to C++11?

I asked it also on SO,

http://stackoverflow.com/questions/8274451/well-how-does-the-custom-deleter-of-stdunique-ptr-work

Cheers,

- Alf
 
J

Juha Nieminen

Alf P. Steinbach said:
I'm looking at N3290, which I believe is identical to the final standard.

And there std::unique_ptr stores a deleter, which can be function object
or a function pointer or whatever, specified in constructor call.

If std::unique_ptr is indeed specified to store a pointer to a deleter
function (or a deleter object), then it's a bit disappointing.

When I read that std::unique_ptr would be a better replacement for
std::auto_ptr, I was assuming that it's a smart pointer that is as
efficient as a raw pointer (at least in terms of size), which destroys
the managed object automatically (the advantage over std::auto_ptr being
that it's safe to use in data containers such as std::vector, which may
move its elements around when reallocating, inserting in the middle or
eg. sorting).

However, if std::unique_ptr is (at least) the size of *two* pointers,
it immediately becomes inferior to a raw pointer in terms of efficiency.
While in many/most cases that doesn't matter, it's a bummer in situations
where it would.

I understand the advantage of having a pointer to a deleter in the smart
pointer object: It allows safely deleting an object even in contexts where
the class has only been declared (rather than defined), plus some other
situations (such as using a specialized deleter for the managed object).
However, these situations are quite unusual in practice, and I find it
questionable why one has to pay for something that isn't used, which goes
against one of the core principles of C++. (After all, this principle is
the very reason why eg. classes do not have virtual tables by default,
but only if there specifically are virtual functions. You don't have to
pay for what you don't use.)

Or is std::unique_ptr able to elide storing a deleter pointer (eg. by
using some kind of empty base class optimization trick) in cases where
no deleter is explicitly specified?
 
A

Alf P. Steinbach

If std::unique_ptr is indeed specified to store a pointer to a deleter
function (or a deleter object), then it's a bit disappointing.

When I read that std::unique_ptr would be a better replacement for
std::auto_ptr, I was assuming that it's a smart pointer that is as
efficient as a raw pointer (at least in terms of size), which destroys
the managed object automatically (the advantage over std::auto_ptr being
that it's safe to use in data containers such as std::vector, which may
move its elements around when reallocating, inserting in the middle or
eg. sorting).

However, if std::unique_ptr is (at least) the size of *two* pointers,
it immediately becomes inferior to a raw pointer in terms of efficiency.
While in many/most cases that doesn't matter, it's a bummer in situations
where it would.

Yes, that is the case, sorry.
I understand the advantage of having a pointer to a deleter in the smart
pointer object: It allows safely deleting an object even in contexts where
the class has only been declared (rather than defined), plus some other
situations (such as using a specialized deleter for the managed object).
However, these situations are quite unusual in practice, and I find it
questionable why one has to pay for something that isn't used, which goes
against one of the core principles of C++. (After all, this principle is
the very reason why eg. classes do not have virtual tables by default,
but only if there specifically are virtual functions. You don't have to
pay for what you don't use.)

I agree.

Or is std::unique_ptr able to elide storing a deleter pointer (eg. by
using some kind of empty base class optimization trick) in cases where
no deleter is explicitly specified?

I'm not sure.

Anyway I misunderstood the deleter thing. As someone pointed out on SO,
with unique_ptr the deleter type is a template argument for the /type/.
And it doesn't work (directly) for the usage where you allocate as
derived and delete as base, without virtual destructor.

However, I managed to browbeat the unique_ptr functionality into doing
that, by applying a little type erasure: :)


<code>
#include <iostream>
#include <memory> // std::unique_ptr
#include <functional> // function
#include <utility> // move
#include <string>
using namespace std;

class Base
{
public:
Base() { cout << "Base:<init>" << endl; }
~Base() { cout << "Base::<destroy>" << endl; }
};

class Derived
: public Base
{
public:
Derived() { cout << "Derived::<init>" << endl; }
~Derived() { cout << "Derived::<destroy>" << endl; }
};

class BoundDeleter
{
private:
typedef void (*DeleteFunc)( void* p );

DeleteFunc deleteFunc_;
void* pObject_;

template< class Type >
static void deleteFuncImpl( void* p )
{
delete static_cast< Type* >( p );
}

public:
template< class Type >
BoundDeleter( Type* pObject )
: deleteFunc_( &deleteFuncImpl< Type > )
, pObject_( pObject )
{}

BoundDeleter( BoundDeleter&& other )
: deleteFunc_( move( other.deleteFunc_ ) )
, pObject_( move( other.pObject_ ) )
{}

void operator() (void*) const
{
deleteFunc_( pObject_ );
}
};

template< class Type >
class SafeCleanupUniquePtr
: public unique_ptr< Type, BoundDeleter >
{
public:
typedef unique_ptr< Type, BoundDeleter > Base;

template< class ActualType >
SafeCleanupUniquePtr( ActualType* p )
: Base( p, BoundDeleter( p ) )
{}

template< class Other >
SafeCleanupUniquePtr( SafeCleanupUniquePtr< Other >&& other )
: Base( move( other ) )
{}
};

int main()
{
SafeCleanupUniquePtr< Base > p( new Derived );
}
</code>


Cheers,

- Alf
 
J

Juha Nieminen

Alf P. Steinbach said:
I'm not sure.

I did what I should have done earlier, and tested it. In a 64-bit system
using gcc 4.6.1 both sizeof(int*) and sizeof(std::unique_ptr<int>) are 8,
so I suppose it is smart enough to avoid reserving space for an unneeded
deleter pointer.

That means I was too hasty to be disappointed with std::unique_ptr.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top