Virtual Destructors and array of objects

Discussion in 'C++' started by amparikh@gmail.com, Jun 7, 2005.

  1. Guest

    Ok, my question is not about Virtual destructors and why, but more on
    the significance.
    Generally we have a virtual destructor in the base class ( and
    inadvertently in the derived class) so that you can delete a
    derived-class object via a base-class pointer...So, the correct
    destructor(s) gets invoked(the derived class one in particular) and the
    correct amount of memory is also released.

    But if the above is true, why isnt it a good practice to allocate an
    array of derived objects via base class pointer and then delete them
    via this base class pointer using delete[] ptr ?

    I am going through the FAQ about placement new and how the whole thing
    is implemented so as to keep track of the number of objects and
    therefore call each corresponding destructor.

    So if deleting derived objects via base pointer works for one object,
    then I dont know why it wouldnt work for deleting an array of derived
    objects via base pointer.

    Maybe I am missing something or maybe my understanding is not
    complete...can anyone throw more light on this.

    Thanks.
    , Jun 7, 2005
    #1
    1. Advertising

  2. <> skrev i en meddelelse
    news:...
    > Ok, my question is not about Virtual destructors and why, but more on
    > the significance.
    > Generally we have a virtual destructor in the base class ( and
    > inadvertently in the derived class) so that you can delete a
    > derived-class object via a base-class pointer...So, the correct
    > destructor(s) gets invoked(the derived class one in particular) and the
    > correct amount of memory is also released.
    >
    > But if the above is true, why isnt it a good practice to allocate an
    > array of derived objects via base class pointer and then delete them
    > via this base class pointer using delete[] ptr ?
    >
    > I am going through the FAQ about placement new and how the whole thing
    > is implemented so as to keep track of the number of objects and
    > therefore call each corresponding destructor.
    >
    > So if deleting derived objects via base pointer works for one object,
    > then I dont know why it wouldnt work for deleting an array of derived
    > objects via base pointer.


    Because the standard says so.

    >
    > Maybe I am missing something or maybe my understanding is not
    > complete...can anyone throw more light on this.
    >
    > Thanks.
    >


    See the thread "virtual destructors" in this group. I have given an
    explanation as to why it does not work there.

    /Peter
    Peter Koch Larsen, Jun 7, 2005
    #2
    1. Advertising

  3. wrote:
    > Ok, my question is not about Virtual destructors and why, but more on
    > the significance.
    > Generally we have a virtual destructor in the base class ( and
    > inadvertently in the derived class) so that you can delete a
    > derived-class object via a base-class pointer...So, the correct
    > destructor(s) gets invoked(the derived class one in particular) and the
    > correct amount of memory is also released.


    Right.

    > But if the above is true, why isnt it a good practice to allocate an
    > array of derived objects via base class pointer and then delete them
    > via this base class pointer using delete[] ptr ?


    I don't seem to understand the question. An array of derived objects
    via base class pointer? Could you illustrate it with some code? Do you
    mean

    base *array = new derived[10];

    ? That won't work as soon as you try indexing.

    > I am going through the FAQ about placement new and how the whole thing
    > is implemented so as to keep track of the number of objects and
    > therefore call each corresponding destructor.
    >
    > So if deleting derived objects via base pointer works for one object,
    > then I dont know why it wouldnt work for deleting an array of derived
    > objects via base pointer.


    Deleting an array involves the _size_. The size is obtained from the
    type of the array element...

    > Maybe I am missing something or maybe my understanding is not
    > complete...can anyone throw more light on this.


    I guess you need to realize the difference between the 'delete' and
    'delete[]'. If you need help, just ask (after you've searched all the
    usual places for the explanations).

    V
    Victor Bazarov, Jun 7, 2005
    #3
  4. On 7 Jun 2005 13:41:37 -0700, wrote:

    [snip]
    >why isnt it a good practice to allocate an
    >array of derived objects via base class pointer and then delete them
    >via this base class pointer using delete[] ptr ?


    You can allocate an array of base class pointers and use them
    polymorphically (once you hae allocated their respective objects and
    assigned their respective pointers to the array elements, that is).
    But delete[] will only delete the storage for the pointers themselves,
    not what they point to!

    As others have pointed out (and this is, or should be, a FAQ) one must
    never allocate an array of *objects* polymorphically because each
    array element must have the same size.

    --
    Bob Hairgrove
    Bob Hairgrove, Jun 7, 2005
    #4
  5. Guest

    > But if the above is true, why isnt it a good practice to allocate an
    > array of derived objects via base class pointer and then delete them
    > via this base class pointer using delete[] ptr ?

    I

    don't seem to understand the question. An array of derived objects
    via base class pointer? Could you illustrate it with some code? Do
    you
    mean

    base *array = new derived[10];

    >>>Lets for simplicity assume that I hold this allocated ptr(via new or clone) via a smart ptr that holds the base type. Whenever I want to process something, I cast it to the type of the correct object and use it.

    However when the smart ptr goes out of existence it would just do a
    delete[] ptr.


    ? That won't work as soon as you try indexing.
    >>>this I agree and as I said while processing i cast it to the right



    > I am going through the FAQ about placement new and how the whole thing
    > is implemented so as to keep track of the number of objects and
    > therefore call each corresponding destructor.


    > So if deleting derived objects via base pointer works for one object,
    > then I dont know why it wouldnt work for deleting an array of derived
    > objects via base pointer.



    Deleting an array involves the _size_. The size is obtained from the
    type of the array element...

    >>>thats my question. if it knows how to delete one object and it deletes them correctly, then why cant it interpolate over n number of objects.


    so lets say
    base* ptr = new derived;
    delete ptr; //here it does the right thing if the destructor is
    declared virtual....it calls the right destrcutor and frees the right
    amount of memory.

    now when you do
    delete[] ptr; //from what I understand, it has to get the ACTUAL size
    of the underlying object.

    Wouldnt this be analogous to say something like a dynamic_cast
    operation from a base class pointer to a derived class pointer. Even in
    this case, you need to check the underlying pointer( which is what
    operator delete or dynamic_cast does) and just interploate it over n
    number of objects( as has been demonstarted in the FAQ via placement
    http://www.parashift.com/c -faq-lite/compiler-dependencies.html#faq-38.8)

    > Maybe I am missing something or maybe my understanding is not
    > complete...can anyone throw more light on this.



    I guess you need to realize the difference between the 'delete' and
    'delete[]'. If you need help, just ask (after you've searched all the
    usual places for the explanations).

    >>>I probably do understand them as much required within the current context. Thanks.


    V
    , Jun 7, 2005
    #5
  6. Guest

    I am actually allocating all objects of the same size, the derived
    class. They arent of different sizes(or different types of derived
    classes)
    , Jun 7, 2005
    #6
  7. Howard Guest

    <> wrote in message
    news:...


    >> thats my question. if it knows how to delete one object and it deletes
    >> them correctly, then why cant it interpolate over n number of objects.


    The rules are simply different for arrays. A base class pointer can be used
    polymorphically. But a pointer to an array of objects cannot.

    If you want polymorphic behavior when using an array, then make it an array
    of base class pointers, and allocate (and later delete) each pointer in the
    array individually. (Even better, use a vector of base class pointers!)

    There are plenty of explanations about this behavior in this newsgroup and
    the moderated one. Try a google search for something like "delete array
    derived class objects".

    -Howard
    Howard, Jun 7, 2005
    #7
  8. Peter Julian Guest

    <> wrote in message
    news:...
    > Ok, my question is not about Virtual destructors and why, but more on
    > the significance.
    > Generally we have a virtual destructor in the base class ( and
    > inadvertently in the derived class) so that you can delete a
    > derived-class object via a base-class pointer...So, the correct
    > destructor(s) gets invoked(the derived class one in particular) and the
    > correct amount of memory is also released.
    >
    > But if the above is true, why isnt it a good practice to allocate an
    > array of derived objects via base class pointer and then delete them
    > via this base class pointer using delete[] ptr ?


    Because the array is created at compile-time. You need a container that
    supports run-time compilation. In effect, the goal is not to delete the
    container, but rather, you want to delete the objects within (or in your
    case, the objects pointed to). Its the objects, not the container of
    pointers, that are polymorphic.

    >
    > I am going through the FAQ about placement new and how the whole thing
    > is implemented so as to keep track of the number of objects and
    > therefore call each corresponding destructor.
    >
    > So if deleting derived objects via base pointer works for one object,
    > then I dont know why it wouldnt work for deleting an array of derived
    > objects via base pointer.


    There is a huge difference since a base pointer to an object is potentially
    a derivative. An array knows no derivatives (its a compile time creature).
    Thats why vectors, deques, stacks, list, map, sets where created. So that
    the object, not the container, has invocable and callable behaviours.

    >
    > Maybe I am missing something or maybe my understanding is not
    > complete...can anyone throw more light on this.
    >


    You are on the right track.
    Peter Julian, Jun 7, 2005
    #8
  9. Peter Julian Guest

    <> wrote in message
    news:...
    > I am actually allocating all objects of the same size, the derived
    > class. They arent of different sizes(or different types of derived
    > classes)
    >


    No you are not, you are allocating objects but storing pointers. the array
    is not an array of objects. If you delete the array, you only delete the
    object's pointers. In fact, the same goes for any type of container:

    #include <iostream>
    #include <vector>

    class Base
    {
    int m_b;
    public:
    Base(int n) : m_b(n) { std::cout << "Base ctor\n"; }
    virtual ~Base() { std::cout << "\nBase d~tor"; }
    };

    class Derived : public Base
    {
    public:
    Derived(int n) : Base(n) { std::cout << "Derived ctor\n"; }
    ~Derived() { std::cout << "\nDerived d~tor"; }
    };


    int main(int argc, char* argv[])
    {
    std::vector< Base* > v_ptr_bases;

    for (int i = 0; i < 5; ++i)
    {
    v_ptr_bases.push_back(new Derived(i));
    }

    // v_ptr_bases.clear(); // no way, those are pointers

    typedef std::vector< Base* >::iterator VIter;
    for (VIter it = v_ptr_bases.begin(); it != v_ptr_bases.end(); ++it)
    {
    delete *it;
    }

    return 0;
    }

    /*
    Base ctor
    Derived ctor
    Base ctor
    Derived ctor
    Base ctor
    Derived ctor
    Base ctor
    Derived ctor
    Base ctor
    Derived ctor

    Derived d~tor
    Base d~tor
    Derived d~tor
    Base d~tor
    Derived d~tor
    Base d~tor
    Derived d~tor
    Base d~tor
    Derived d~tor
    Base d~tor
    */

    There is no doubt that an auto pointer saves the day since manual
    deallocation is no longer needed. But its still relevant to acknowledge that
    a container of pointers is not a container of objects.
    Peter Julian, Jun 8, 2005
    #9
  10. Alan Johnson Guest

    Peter Julian wrote:
    > There is no doubt that an auto pointer saves the day since manual
    > deallocation is no longer needed. But its still relevant to acknowledge that
    > a container of pointers is not a container of objects.
    >


    I'm not sure if you were suggesting this or not, but it is worth noting
    that std::auto_ptr does NOT meet the requirements to be stored in any of
    the standard containers.
    Alan Johnson, Jun 8, 2005
    #10
  11. Me Guest

    The important thing to know is that C++ is a value based language, it
    doesn't behave like other languages where you can do this (all their
    objects are really pointers to objects, they just hide that from you):

    struct A { int a; };

    struct B : A { float f; };

    vector<A> v;
    B b;

    //vector<A>::push_back(const A &);
    v.push_back(b);

    ^-- What happens here is called slicing. In the call to push_back, b
    gets downcasted to an A object and just copies the A object of b over
    to the end of the vector. Slicing is considered to be a bad thing in
    all but a few cases as you lose certain information. Agree with me so
    far?

    A a;
    //A::eek:perator=(const A &);
    a = b;

    ^-- Slicing also happens with regular objects because that's what the
    regular assignment operator is expected to look like.

    A arr[10];
    //arr's index operator returns an A &, this calls the assignment
    operator above
    arr[2] = b;

    A *narr = new A[10];
    //ditto
    narr[2] = b;

    ^-- Slicing also happens when using plain arrays of regular objects. So
    you can see there is no way to store a full B into an array of A,
    therefore an array of type A can only hold objects of type A (or a
    const/volatile compatible A).

    The standard doesn't want to bend over backwards to handle the rare
    case of where you write a correct assignment operator that somehow
    handles slicing because doing that requires the base and derived class
    to be the same size (rare in general and not guaranteed) and in your
    case, involves a memcpy to copy the vtable over (which is undefined and
    assumes your implementation even uses vtables).

    Not all is lost, this code is correct, see if you can figure out why:

    vector<A*> v2();

    // this is ok
    v2.push_back(new B);
    delete v2[0]; // call's B's destructor

    // this is ok
    A **arr2 = new A*[1];
    arr2[0] = new B;
    delete arr2[0] // calls B's destructor
    delete [] arr2;
    Me, Jun 8, 2005
    #11
    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. qazmlp
    Replies:
    7
    Views:
    484
    Howard
    Jul 27, 2004
  2. Ruben Campos
    Replies:
    5
    Views:
    4,148
    Ron Natalie
    Apr 7, 2005
  3. heted7
    Replies:
    33
    Views:
    1,020
    Chris Dearlove
    May 12, 2005
  4. Replies:
    3
    Views:
    365
    Nitin Motgi
    Jan 31, 2006
  5. Replies:
    2
    Views:
    349
    Diego Martins
    May 19, 2006
Loading...

Share This Page