Virtual Destructor

Discussion in 'C++' started by Prawit Chaivong, Jun 1, 2005.

  1. Hi All,

    There is code here.
    ------------------------------------------------------------------
    class Base{
    public:
    Base(){}
    virtual ~Base(){}
    private:
    int a;
    };

    class Derived : public Base{
    public:
    Derived(){}
    virtual ~Derived(){}
    private:
    int b;
    };

    int main()
    {
    Base* pb = new Derived[10];
    delete[] pb; // <-- crash HERE.
    return 0;
    }
    -------------------------------------------------------------------
    >From code, I know that I shouldn't delete Derived object by its parent

    pointer. Because size of both are not similar.

    But my question is when I remove keyword 'virtual' out of
    destructor(both classes). This program is not crash anymore.
    I just want to know why it's not crash if its dtor is not virtual.

    By the way, I use gcc 3.2.3.

    Thank in advance.
    Prawit Chaivong
    Prawit Chaivong, Jun 1, 2005
    #1
    1. Advertising

  2. * Prawit Chaivong:
    >
    > class Base{
    > public:
    > Base(){}
    > virtual ~Base(){}
    > private:
    > int a;
    > };
    >
    > class Derived : public Base{
    > public:
    > Derived(){}
    > virtual ~Derived(){}
    > private:
    > int b;
    > };
    >
    > int main()
    > {
    > Base* pb = new Derived[10];
    > delete[] pb; // <-- crash HERE.
    > return 0;
    > }
    > -------------------------------------------------------------------
    > >From code, I know that I shouldn't delete Derived object by its parent

    > pointer. Because size of both are not similar.
    >
    > But my question is when I remove keyword 'virtual' out of
    > destructor(both classes). This program is not crash anymore.
    > I just want to know why it's not crash if its dtor is not virtual.


    The program is incorrect with or without a virtual destructor.

    You have run up against a flaw in the type system, inherited from C,
    namely that an array is represented by a pointer to its first element.

    To 'delete' an array you must use a pointer of the exact type the
    array was allocated with. The compiler infers the memory size of each
    array element from the pointer type. Using the wrong pointer type you
    essentially lie to the compiler about the element size, so even though
    your program doesn't crash with non-virtual destructor that's just
    accidental: it could just as well crash, or do anything; it's UB.

    Here's another example of this same type system flaw (off the cuff):

    #include <iostream>
    #include <ostream>

    struct A{ virtual int id() const { return 1; } };
    struct B: A { char dummy; virtual int id() const { return 2; } };

    void display( A const* objects, int n )
    {
    for( int i = 0; i < n; ++i )
    {
    std::cout << objects.id() << std::endl;
    }
    }

    int main()
    {
    B const array[3] = {};
    display( array, 3 );
    }

    To get polymorphism in an array, store pointers to the objects.

    --
    A: Because it messes up the order in which people normally read text.
    Q: Why is it such a bad thing?
    A: Top-posting.
    Q: What is the most annoying thing on usenet and in e-mail?
    Alf P. Steinbach, Jun 1, 2005
    #2
    1. Advertising

  3. Yes, I know that this program is incorrect.
    My POINT is I just want to know that why when I use virtual, this
    program suddently crash, whilst non-virtual doesn't.
    Prawit Chaivong, Jun 1, 2005
    #3
  4. Prawit Chaivong

    Jack Klein Guest

    On 31 May 2005 21:49:53 -0700, "Prawit Chaivong" <>
    wrote in comp.lang.c++:

    > Yes, I know that this program is incorrect.
    > My POINT is I just want to know that why when I use virtual, this
    > program suddently crash, whilst non-virtual doesn't.


    You break the rules, you cause undefined behavior. The C++ standard
    does not specify what happens when you cause undefined behavior. It
    could crash, it could seem to work right, it could format your hard
    disk. C++ does not say, does not know, does not care.

    --
    Jack Klein
    Home: http://JK-Technology.Com
    FAQs for
    comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
    comp.lang.c++ http://www.parashift.com/c -faq-lite/
    alt.comp.lang.learn.c-c++
    http://www.contrib.andrew.cmu.edu/~ajo/docs/FAQ-acllc.html
    Jack Klein, Jun 1, 2005
    #4
  5. That's ok if you guys don't want to go deep a little bit further. I'll
    do it alone.
    Because I want to know what's going on under the surface.
    Prawit Chaivong, Jun 1, 2005
    #5
  6. Prawit Chaivong

    Lionel B Guest

    Prawit Chaivong wrote:
    > That's ok if you guys don't want to go deep a little bit further. I'll
    > do it alone.
    > Because I want to know what's going on under the surface.


    Alf P. Steinbach answered your question very precisely: the upshot is that your code (whether the dtor is virtual or
    not) is incorrect and invokes undefined behaviour. If you want to know how your particular compiler handles this
    particular case of undefined behaviour, then... well, why would you want to know that? There's nothing very interesting
    under that surface and hence no-one in this newsgroup is likely to be interested in discussing it.

    --
    Lionel B
    Lionel B, Jun 1, 2005
    #6
  7. Prawit Chaivong

    baumann@pan Guest

    Alf P. Steinbach wrote:
    > * Prawit Chaivong:
    > >
    > > class Base{
    > > public:
    > > Base(){}
    > > virtual ~Base(){}
    > > private:
    > > int a;
    > > };
    > >
    > > class Derived : public Base{
    > > public:
    > > Derived(){}
    > > virtual ~Derived(){}
    > > private:
    > > int b;
    > > };
    > >
    > > int main()
    > > {
    > > Base* pb = new Derived[10];
    > > delete[] pb; // <-- crash HERE.
    > > return 0;
    > > }
    > > -------------------------------------------------------------------
    > > >From code, I know that I shouldn't delete Derived object by its parent

    > > pointer. Because size of both are not similar.
    > >
    > > But my question is when I remove keyword 'virtual' out of
    > > destructor(both classes). This program is not crash anymore.
    > > I just want to know why it's not crash if its dtor is not virtual.

    >
    > The program is incorrect with or without a virtual destructor.
    >
    > You have run up against a flaw in the type system, inherited from C,
    > namely that an array is represented by a pointer to its first element.
    >
    > To 'delete' an array you must use a pointer of the exact type the
    > array was allocated with. The compiler infers the memory size of each
    > array element from the pointer type. Using the wrong pointer type you
    > essentially lie to the compiler about the element size, so even though
    > your program doesn't crash with non-virtual destructor that's just
    > accidental: it could just as well crash, or do anything; it's UB.
    >
    > Here's another example of this same type system flaw (off the cuff):
    >
    > #include <iostream>
    > #include <ostream>
    >
    > struct A{ virtual int id() const { return 1; } };
    > struct B: A { char dummy; virtual int id() const { return 2; } };
    >
    > void display( A const* objects, int n )
    > {
    > for( int i = 0; i < n; ++i )
    > {
    > std::cout << objects.id() << std::endl;
    > }
    > }
    >
    > int main()
    > {
    > B const array[3] = {};
    > display( array, 3 );
    > }
    >
    > To get polymorphism in an array, store pointers to the objects.
    >


    how?
    thanks much!!!!

    > --
    > A: Because it messes up the order in which people normally read text.
    > Q: Why is it such a bad thing?
    > A: Top-posting.
    > Q: What is the most annoying thing on usenet and in e-mail?
    baumann@pan, Jun 1, 2005
    #7
  8. Prawit Chaivong

    baumann@pan Guest

    Alf P. Steinbach wrote:
    > * Prawit Chaivong:
    > >
    > > class Base{
    > > public:
    > > Base(){}
    > > virtual ~Base(){}
    > > private:
    > > int a;
    > > };
    > >
    > > class Derived : public Base{
    > > public:
    > > Derived(){}
    > > virtual ~Derived(){}
    > > private:
    > > int b;
    > > };
    > >
    > > int main()
    > > {
    > > Base* pb = new Derived[10];
    > > delete[] pb; // <-- crash HERE.
    > > return 0;
    > > }
    > > -------------------------------------------------------------------
    > > >From code, I know that I shouldn't delete Derived object by its parent

    > > pointer. Because size of both are not similar.
    > >
    > > But my question is when I remove keyword 'virtual' out of
    > > destructor(both classes). This program is not crash anymore.
    > > I just want to know why it's not crash if its dtor is not virtual.

    >
    > The program is incorrect with or without a virtual destructor.
    >
    > You have run up against a flaw in the type system, inherited from C,
    > namely that an array is represented by a pointer to its first element.
    >
    > To 'delete' an array you must use a pointer of the exact type the
    > array was allocated with. The compiler infers the memory size of each
    > array element from the pointer type. Using the wrong pointer type you
    > essentially lie to the compiler about the element size, so even though
    > your program doesn't crash with non-virtual destructor that's just
    > accidental: it could just as well crash, or do anything; it's UB.
    >
    > Here's another example of this same type system flaw (off the cuff):
    >
    > #include <iostream>
    > #include <ostream>
    >
    > struct A{ virtual int id() const { return 1; } };
    > struct B: A { char dummy; virtual int id() const { return 2; } };
    >
    > void display( A const* objects, int n )
    > {
    > for( int i = 0; i < n; ++i )
    > {
    > std::cout << objects.id() << std::endl;
    > }
    > }
    >
    > int main()
    > {
    > B const array[3] = {};
    > display( array, 3 );
    > }
    >
    > To get polymorphism in an array, store pointers to the objects.
    >



    how To get
    polymorphism in an array?

    thanks much.

    > --
    > A: Because it messes up the order in which people normally read text.
    > Q: Why is it such a bad thing?
    > A: Top-posting.
    > Q: What is the most annoying thing on usenet and in e-mail?
    baumann@pan, Jun 1, 2005
    #8
  9. Prawit Chaivong

    baumann@pan Guest

    Alf P. Steinbach wrote:
    > * Prawit Chaivong:
    > >
    > > class Base{
    > > public:
    > > Base(){}
    > > virtual ~Base(){}
    > > private:
    > > int a;
    > > };
    > >
    > > class Derived : public Base{
    > > public:
    > > Derived(){}
    > > virtual ~Derived(){}
    > > private:
    > > int b;
    > > };
    > >
    > > int main()
    > > {
    > > Base* pb = new Derived[10];
    > > delete[] pb; // <-- crash HERE.
    > > return 0;
    > > }
    > > -------------------------------------------------------------------
    > > >From code, I know that I shouldn't delete Derived object by its parent

    > > pointer. Because size of both are not similar.
    > >
    > > But my question is when I remove keyword 'virtual' out of
    > > destructor(both classes). This program is not crash anymore.
    > > I just want to know why it's not crash if its dtor is not virtual.

    >
    > The program is incorrect with or without a virtual destructor.
    >
    > You have run up against a flaw in the type system, inherited from C,
    > namely that an array is represented by a pointer to its first element.
    >
    > To 'delete' an array you must use a pointer of the exact type the
    > array was allocated with. The compiler infers the memory size of each
    > array element from the pointer type. Using the wrong pointer type you
    > essentially lie to the compiler about the element size, so even though
    > your program doesn't crash with non-virtual destructor that's just
    > accidental: it could just as well crash, or do anything; it's UB.
    >
    > Here's another example of this same type system flaw (off the cuff):
    >
    > #include <iostream>
    > #include <ostream>
    >
    > struct A{ virtual int id() const { return 1; } };
    > struct B: A { char dummy; virtual int id() const { return 2; } };
    >

    if display function refer objects as an array, how to get
    polymorphism??

    thanks
    much!!!!!!!!!!!!!!!!!!!!!

    baumann@pan

    > void display( A const* objects, int n )
    > {
    > for( int i = 0; i < n; ++i )
    > {
    > std::cout << objects.id() << std::endl;
    > }
    > }
    >
    > int main()
    > {
    > B const array[3] = {};
    > display( array, 3 );
    > }
    >
    > To get polymorphism in an array, store pointers to the objects.
    >
    > --
    > A: Because it messes up the order in which people normally read text.
    > Q: Why is it such a bad thing?
    > A: Top-posting.
    > Q: What is the most annoying thing on usenet and in e-mail?
    baumann@pan, Jun 1, 2005
    #9
  10. * baumann@pan:
    >
    > if display function refer objects as an array, how to get
    > polymorphism??


    Uhm, first, it's not necessary to post essentially the same
    question three times. ;-)

    Off the cuff (that means untested, never touched by dirty
    compiler's hands), here's some code:

    #include <iostream> // std::cout
    #include <ostream> // <<, std::endl
    #include <memory> // std::auto_ptr
    #include <vector> // std::vector

    struct A { virtual int id() const { return 1; } };
    struct B: A { virtual int id() const { return 2; } }

    // Encapsulate array in a class for safe deallocation.
    class ArrayOfA
    {
    private:
    std::vector<A*> myItems;
    public:
    A(): myRawArray( 0 ) {}

    ~ArrayOfA()
    {
    for( std::size_t i = 0; i < myItems.size(); ++i )
    {
    delete myItems.at( i );
    }
    }

    A& at( int i ) { return myItems.at( i ); }
    A const& at( int i ) const { return myItems.at( i ); }
    int i size() const{ return static_cast<int>( myItems.size() ); }

    // Add operator[] here if you want that; I don't.
    // Using 'at' is much safer than the []-notation.

    void add( std::auto_ptr<A> anItem )
    {
    myItems.push_back( anItem.get() );
    }
    }

    void display( ArrayOfA const& items )
    {
    for( int i = 0; i < items.size(); ++i )
    {
    std::cout << items.at( i ).id() << std::endl;
    }
    }

    ArrayOfA hodgePodgeArray()
    {
    ArrayOfA array;

    array.add( new A );
    array.add( new B );
    array.add( new A );
    array.add( new B );
    array.add( new B );
    return array;
    }

    int main()
    {
    display( hodgePodgeArray() );
    }

    This code has a theoretically big problem: it doesn't transfer ownership
    of the pointers when you assign or copy construct ArrayOfA, so if you're
    not careful you might end up deleting the same object twice, which is UB.

    To address that issue you might want to make the vector elements
    boost::shared_ptr<A> (there are also other more DIY solutions);
    unfortunately std::auto_ptr<A> can't be used since it's not copyable.

    Check out the Boost library at <url: http://www.boost.org>.

    --
    A: Because it messes up the order in which people normally read text.
    Q: Why is it such a bad thing?
    A: Top-posting.
    Q: What is the most annoying thing on usenet and in e-mail?
    Alf P. Steinbach, Jun 1, 2005
    #10
  11. Prawit Chaivong

    Max M. Guest

    Alf P. Steinbach wrote:
    > void add( std::auto_ptr<A> anItem )
    > {
    > myItems.push_back( anItem.get() );


    You meant 'anItem.release()' here, didn't you?

    Max
    Max M., Jun 1, 2005
    #11
  12. * Max M.:
    > Alf P. Steinbach wrote:
    > > void add( std::auto_ptr<A> anItem )
    > > {
    > > myItems.push_back( anItem.get() );

    >
    > You meant 'anItem.release()' here, didn't you?


    Yes and no.

    I did mean 'get', but I forgot to add a 'release' call after
    the 'push_back'.

    I.e., if the 'push_back' succeeds, then release the darned
    pointer, where the 'if' is taken care of by exception or not.

    Thanks,

    - Alf

    --
    A: Because it messes up the order in which people normally read text.
    Q: Why is it such a bad thing?
    A: Top-posting.
    Q: What is the most annoying thing on usenet and in e-mail?
    Alf P. Steinbach, Jun 1, 2005
    #12
  13. "Prawit Chaivong" <> skrev i en meddelelse
    news:...
    > That's ok if you guys don't want to go deep a little bit further. I'll
    > do it alone.
    > Because I want to know what's going on under the surface.
    >

    The answer is that for classes with virtual functions, your compiler creates
    a hidden member in each object it creates - the vtable. This table contains
    (presumably among other things) an array of function pointers. The vtable is
    located somewhere in the object, for example in the beginning of the object.
    Now when the array is deleted, something like this happens:

    for (i = 0; i < 10; ++i)
    {
    ptr_to_destructor _function = pb.__hidden_vtable[0];
    call ptr_to_destructor _function(pb + 1);
    }

    Now, for the second element the function looked up will contain garbage, and
    so the call to the destructor will be at some random adress, causing the
    crash.

    /Peter
    Peter Koch Larsen, Jun 1, 2005
    #13
  14. Prawit Chaivong

    Fraser Ross Guest

    The OP wanted polymorphism in an array which you said can be done, then you
    gave an example of doing it in a vector. Although it can be done with an
    array it is tedious to do the setting up of the array. The objects would
    have to be created and destroyed elsewhere.

    Fraser.
    Fraser Ross, Jun 1, 2005
    #14
  15. * Fraser Ross:
    > The OP wanted polymorphism in an array which you said can be done, then you
    > gave an example of doing it in a vector.


    What do you think a vector is?


    > Although it can be done with an [raw] array it is tedious to do
    > the setting up of the array.


    Nope, but some other things are tedious, hence the std::vector,
    which takes care of all that tedious stuff, and does it using
    code that's almost guaranteed to be correct.

    Always use a std::vector (or e.g. Boost arrays) instead of raw
    arrays.

    If you can.


    > The objects would have to be created and destroyed elsewhere.


    That statement doesn't make sense to me, i.e., you've probably
    misunderstood something in addition to the misunderstandings above.

    Cheers,

    - Alf

    --
    A: Because it messes up the order in which people normally read text.
    Q: Why is it such a bad thing?
    A: Top-posting.
    Q: What is the most annoying thing on usenet and in e-mail?
    Alf P. Steinbach, Jun 1, 2005
    #15
  16. Fraser Ross wrote:
    >
    > The OP wanted polymorphism in an array which you said can be done, then you
    > gave an example of doing it in a vector. Although it can be done with an
    > array it is tedious to do the setting up of the array. The objects would
    > have to be created and destroyed elsewhere.


    And that differs to the std::vector solution exactly how?
    The important point is: If you want polymorphsm your data structure
    needs to hold pointers. If that data structure is an array or if it
    is a std::vector makes no difference.

    --
    Karl Heinz Buchegger
    Karl Heinz Buchegger, Jun 1, 2005
    #16
  17. Prawit Chaivong

    Fraser Ross Guest

    "Karl Heinz Buchegger"
    > Fraser Ross wrote:
    > >
    > > The OP wanted polymorphism in an array which you said can be done, then

    you
    > > gave an example of doing it in a vector. Although it can be done with

    an
    > > array it is tedious to do the setting up of the array. The objects

    would
    > > have to be created and destroyed elsewhere.

    >
    > And that differs to the std::vector solution exactly how?
    > The important point is: If you want polymorphsm your data structure
    > needs to hold pointers. If that data structure is an array or if it
    > is a std::vector makes no difference.


    I see now std::vector has the same problem with deallocation as an array.
    Alfs program has UB. auto_ptr/shared_ptr won't work either. I don't know
    the boost array.

    I was thinking about an array set up like this:

    class B {}
    class D :B {}

    B b[2];
    D d[3];

    B * array[5];
    array[0]=&d[0];
    array[1]=&b[1];
    array[2]=&d[2];
    array[3]=&b[0];
    array[4]=&d[1];

    The destruction of the elements pointed to is independent of the array.

    Fraser.
    Fraser Ross, Jun 1, 2005
    #17
  18. Prawit Chaivong

    Fraser Ross Guest

    "Fraser Ross"
    > I see now std::vector has the same problem with deallocation as an array.
    > Alfs program has UB. auto_ptr/shared_ptr won't work either. I don't know
    > the boost array.


    No thats a mistake. The elements destructors are doing the deallocation ok.

    Fraser.
    Fraser Ross, Jun 1, 2005
    #18
  19. Prawit Chaivong

    Ron Natalie Guest

    Prawit Chaivong wrote:
    > Yes, I know that this program is incorrect.
    > My POINT is I just want to know that why when I use virtual, this
    > program suddently crash, whilst non-virtual doesn't.
    >

    It's silly to try to distinguish why you get two different
    behaviors when both cases are undefined behavior, but if we
    want to play that mental game:

    The case without the virtual destructor invokes ~Base.
    The case with the virtual destructor attempts to invoke
    the derived class destructor on each item, HOWEVER, the
    size of the object is screwed up because ((Base*)bp)[1]
    is a different address than ((Derived*)bp)[1]
    Ron Natalie, Jun 1, 2005
    #19
  20. * Fraser Ross:
    > "Fraser Ross"
    > > I see now std::vector has the same problem with deallocation as an array.
    > > Alfs program has UB. auto_ptr/shared_ptr won't work either. I don't know
    > > the boost array.

    >
    > No thats a mistake. The elements destructors are doing the deallocation ok.


    Note that I forgot to type '*' in the accessors. Bunger. But anyone
    trying to compile the thing would see that.

    --
    A: Because it messes up the order in which people normally read text.
    Q: Why is it such a bad thing?
    A: Top-posting.
    Q: What is the most annoying thing on usenet and in e-mail?
    Alf P. Steinbach, Jun 1, 2005
    #20
    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. Calvin Lai
    Replies:
    7
    Views:
    552
    Calvin Lai
    Dec 18, 2003
  2. Chunhui Han
    Replies:
    2
    Views:
    501
  3. frs
    Replies:
    20
    Views:
    752
    Alf P. Steinbach
    Sep 21, 2005
  4. arun
    Replies:
    2
    Views:
    543
    benben
    Jun 13, 2006
  5. Jimmy Hartzell
    Replies:
    0
    Views:
    418
    Jimmy Hartzell
    May 19, 2008
Loading...

Share This Page