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

Discussion in 'C++' started by Qi, Nov 24, 2011.

  1. Qi

    Qi Guest

    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


    --
    WQ
    Qi, Nov 24, 2011
    #1
    1. Advertising

  2. On 24.11.2011 10:20, Qi wrote:
    > 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
    Alf P. Steinbach, Nov 24, 2011
    #2
    1. Advertising

  3. On 24/11/11 10:20, Qi wrote:
    > 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()
    {
    }
    Vladimir Jovic, Nov 24, 2011
    #3
  4. Qi

    SG Guest

    On 24 Nov., 10:20, Qi wrote:
    > 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
    SG, Nov 24, 2011
    #4
  5. Qi

    Qi Guest

    On 2011-11-24 20:50, SG wrote:
    > 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.


    --
    WQ
    Qi, Nov 24, 2011
    #5
  6. Qi

    Qi Guest

    On 2011-11-24 17:35, Alf P. Steinbach wrote:
    >
    > 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.


    --
    WQ
    Qi, Nov 24, 2011
    #6
  7. Qi

    Qi Guest

    On 2011-11-24 17:45, Vladimir Jovic wrote:
    > // 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.


    --
    WQ
    Qi, Nov 24, 2011
    #7
  8. Qi

    Marc Guest

    Qi wrote:

    > 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.
    Marc, Nov 24, 2011
    #8
  9. Qi

    Lee Steven Guest

    On Nov 24, 5:20 am, Qi <> wrote:
    > 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
    >
    > --
    > WQ


    Here, I think If A has no virtual dtor, memory leak!
    Lee Steven, Nov 25, 2011
    #9
  10. Alf P. Steinbach <> wrote:
    > 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.
    Juha Nieminen, Nov 25, 2011
    #10
  11. On 25.11.2011 19:30, Juha Nieminen wrote:
    > Alf P. Steinbach<> wrote:
    >> 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.


    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
    Alf P. Steinbach, Nov 25, 2011
    #11
  12. On 25.11.2011 22:25, Alf P. Steinbach wrote:
    > On 25.11.2011 19:30, Juha Nieminen wrote:
    >> Alf P. Steinbach<> wrote:
    >>> 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.

    >
    > 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
    Alf P. Steinbach, Nov 25, 2011
    #12
  13. Qi

    Joe keane Guest

    fire them
    Joe keane, Nov 25, 2011
    #13
  14. Alf P. Steinbach <> wrote:
    > 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?
    Juha Nieminen, Nov 25, 2011
    #14
  15. Qi

    Ian Collins Guest

    On 11/26/11 11:21 AM, Joe keane wrote:
    > fire them


    Fire who? There isn't a premium charged for quoting context.

    --
    Ian Collins
    Ian Collins, Nov 25, 2011
    #15
  16. On 25.11.2011 23:30, Juha Nieminen wrote:
    > Alf P. Steinbach<> wrote:
    >> 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.


    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
    Alf P. Steinbach, Nov 26, 2011
    #16
  17. Alf P. Steinbach <> wrote:
    >> 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.


    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.
    Juha Nieminen, Nov 26, 2011
    #17
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Chunhui Han
    Replies:
    2
    Views:
    493
  2. qazmlp
    Replies:
    1
    Views:
    558
    qazmlp
    Apr 10, 2005
  3. frs
    Replies:
    20
    Views:
    737
    Alf P. Steinbach
    Sep 21, 2005
  4. LAvoisieR
    Replies:
    8
    Views:
    421
    LAvoisieR
    Oct 27, 2005
  5. AommiK

    Absense of bool

    AommiK, Nov 3, 2007, in forum: C Programming
    Replies:
    73
    Views:
    1,455
    Chris Hills
    Nov 26, 2007
Loading...

Share This Page