Explicit destructor calls from inside base class destructor

Discussion in 'C++' started by frs, Sep 18, 2005.

  1. frs

    frs Guest

    For memory economization, I need to get rid if the virtual
    destructor. Under this constraint I get caught up in a design,
    where I need to call a destructor of a derived class from a
    base class. Briefly it could be displayed like the code below.
    It ends up deleting the member 'x' of 'B' twice. Why? Is there
    a way around?

    Thanks

    Frank

    =====================================================================
    #include<iostream>
    using namespace std;

    struct TypeX { ~TypeX() { cerr << "destructed\n"; }
    struct A { ~A(); }
    struct B : public A {
    TypeX x;
    ~B();
    }

    A::~A() {
    // at this point, ~A knows, that 'this' points is in fact to a 'B'
    // (it does! no need to say 'type-switches are bad' - there's no
    other way)
    delete &(static_cast<B*>(this)->x);
    }

    int
    main(int, char**)
    {
    A* a = new B;
    cerr << "before\n";
    // somewhere, there might be someone deleting 'a'
    delete a;
    cerr << "after\n";
    }
    frs, Sep 18, 2005
    #1
    1. Advertising

  2. * frs:
    > For memory economization, I need to get rid if the virtual
    > destructor. Under this constraint I get caught up in a design,
    > where I need to call a destructor of a derived class from a
    > base class.


    Already you have told enough.

    Don't.

    --
    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, Sep 18, 2005
    #2
    1. Advertising

  3. frs wrote:
    > For memory economization, I need to get rid if the virtual
    > destructor. Under this constraint I get caught up in a design,
    > where I need to call a destructor of a derived class from a
    > base class. Briefly it could be displayed like the code below.
    > It ends up deleting the member 'x' of 'B' twice. Why? Is there
    > a way around?
    >
    > Thanks
    >
    > Frank
    >
    > =====================================================================
    > #include<iostream>
    > using namespace std;
    >
    > struct TypeX { ~TypeX() { cerr << "destructed\n"; }
    > struct A { ~A(); }
    > struct B : public A {
    > TypeX x;
    > ~B();
    > }
    >
    > A::~A() {
    > // at this point, ~A knows, that 'this' points is in fact to a 'B'
    > // (it does! no need to say 'type-switches are bad' - there's no
    > other way)
    > delete &(static_cast<B*>(this)->x);


    ITYM

    static_cast<B*>(this)->x::~TypeX();

    but really, I think you should choose a different language to C++ if you
    memory constratints are as titght as you say they are. Perhaps you would
    be better of with C.

    john
    John Harrison, Sep 18, 2005
    #3
  4. frs

    Axter Guest

    frs wrote:
    > For memory economization, I need to get rid if the virtual
    > destructor. Under this constraint I get caught up in a design,
    > where I need to call a destructor of a derived class from a
    > base class. Briefly it could be displayed like the code below.
    > It ends up deleting the member 'x' of 'B' twice. Why? Is there
    > a way around?
    >
    > Thanks
    >
    > Frank
    >
    > =====================================================================
    > #include<iostream>
    > using namespace std;
    >
    > struct TypeX { ~TypeX() { cerr << "destructed\n"; }
    > struct A { ~A(); }
    > struct B : public A {
    > TypeX x;
    > ~B();
    > }
    >
    > A::~A() {
    > // at this point, ~A knows, that 'this' points is in fact to a 'B'
    > // (it does! no need to say 'type-switches are bad' - there's no
    > other way)
    > delete &(static_cast<B*>(this)->x);
    > }
    >
    > int
    > main(int, char**)
    > {
    > A* a = new B;
    > cerr << "before\n";
    > // somewhere, there might be someone deleting 'a'
    > delete a;
    > cerr << "after\n";
    > }


    You can avoid using a virtual destructor if you use the following smart
    pointer (cow_ptr) class:
    http://code.axter.com/cow_ptr.h

    However, IMHO, you're not going to save much by removing the virtual
    destructor.
    Axter, Sep 18, 2005
    #4
  5. frs

    Kai-Uwe Bux Guest

    frs wrote:

    > For memory economization, I need to get rid if the virtual
    > destructor. Under this constraint I get caught up in a design,
    > where I need to call a destructor of a derived class from a
    > base class. Briefly it could be displayed like the code below.
    > It ends up deleting the member 'x' of 'B' twice. Why?


    The destructor of B calls the destructors of all base classes to take care
    of the corresponding subobjects. Thus, the destructor of A is called again.
    I would be inclined to assume that this invokes undefined behavior. Anyway,
    the following code shows an infinite loop on my machine:

    #include <iostream>

    struct A {
    ~A();
    };

    struct B : public A {

    ~B ( void ) {
    std::cout << "desctructing B\n";
    }

    };

    A::~A ( void ) {
    std::cout << "desctructing A\n";
    static_cast<B*>( this )->~B();
    }

    int main ( void ) {
    B b;
    }


    > Is there a way around?


    Maybe. How is anybody to tell? You did not offer that much of information
    regarding what you actually want to accomplish. An item of particular
    interest would be: how do you know that objects of type A* are dynamically
    of type B*; and if you know that, do you really need runtime polymorphism?


    [code snipped]


    Best

    Kai-Uwe Bux
    Kai-Uwe Bux, Sep 18, 2005
    #5
  6. John Harrison wrote:
    > frs wrote:
    >
    >> For memory economization, I need to get rid if the virtual
    >> destructor. Under this constraint I get caught up in a design,
    >> where I need to call a destructor of a derived class from a
    >> base class. Briefly it could be displayed like the code below.
    >> It ends up deleting the member 'x' of 'B' twice. Why? Is there
    >> a way around?
    >>
    >> Thanks
    >>
    >> Frank
    >>
    >> =====================================================================
    >> #include<iostream>
    >> using namespace std;
    >>
    >> struct TypeX { ~TypeX() { cerr << "destructed\n"; }
    >> struct A { ~A(); }
    >> struct B : public A {
    >> TypeX x;
    >> ~B();
    >> }
    >>
    >> A::~A() {
    >> // at this point, ~A knows, that 'this' points is in fact to a 'B'
    >> // (it does! no need to say 'type-switches are bad' - there's no
    >> other way)
    >> delete &(static_cast<B*>(this)->x);

    >
    >
    > ITYM
    >
    > static_cast<B*>(this)->x::~TypeX();
    >


    Soory, didn't check that the above compiled. Here's what I really meant.

    #include<iostream>
    using namespace std;

    struct TypeX { ~TypeX() { cerr << "destructed\n"; } };
    struct A { ~A(); };
    struct B : public A {
    TypeX x;
    ~B();
    };

    A::~A() {
    static_cast<B*>(this)->x.~TypeX();
    }

    int
    main(int, char**)
    {
    A* a = new B;
    cerr << "before\n";
    // somewhere, there might be someone deleting 'a'
    delete a;
    cerr << "after\n";
    }

    output is

    before
    destructed
    after

    John
    John Harrison, Sep 18, 2005
    #6
  7. "frs" <> wrote in message
    news:...
    > For memory economization, I need to get rid if the virtual
    > destructor. Under this constraint I get caught up in a design,
    > where I need to call a destructor of a derived class from a
    > base class. Briefly it could be displayed like the code below.
    > It ends up deleting the member 'x' of 'B' twice. Why? Is there
    > a way around?
    >
    > Thanks
    >
    > Frank


    From stepping through the code first you are deleting x explicitly then the
    destructor of A also deletes it implicitly.

    i.e. your problem seems to act as if you did the following

    main()
    {
    TypeX x;

    delete &x;

    }



    Try using a pointer to TypeX and see if it works better or not.

    i.e.

    TypeX* x;

    and change

    delete &(static_cast<B*>(this)->x); to

    delete (static_cast<B*>(this)->x);





    What seems to be the problem as I pointed out earlier is that you delete a
    non-pointer and then the destructor deletes it too... it doesn't know that
    you already deleted it and probably because the method doesn't seem very
    "stable" in the sense its kinda forcing the language to do something that
    doesn't seem natural(IMO). Maybe you should rething the design and see if
    you can't make it more natural? Maybe you can override new and delete for
    your needs?



    Jon
    Jon Slaughter, Sep 18, 2005
    #7
  8. frs

    Rolf Magnus Guest

    frs wrote:

    > For memory economization, I need to get rid if the virtual
    > destructor.


    Does a virtual destructor really increase your memory needs so much? Which
    platform are you writing that code for, if a few bytes of memory are too
    much?

    > Under this constraint I get caught up in a design,
    > where I need to call a destructor of a derived class from a
    > base class. Briefly it could be displayed like the code below.
    > It ends up deleting the member 'x' of 'B' twice. Why?


    Because when the end of an object's life is reached, the destructor gets
    called automatically.

    > Is there a way around?


    Well, don't use polymorphism if the (actually quite small) overhead is too
    much for your platform.

    > =====================================================================
    > #include<iostream>
    > using namespace std;
    >
    > struct TypeX { ~TypeX() { cerr << "destructed\n"; }
    > struct A { ~A(); }
    > struct B : public A {
    > TypeX x;
    > ~B();
    > }
    >
    > A::~A() {
    > // at this point, ~A knows, that 'this' points is in fact to a 'B'
    > // (it does! no need to say 'type-switches are bad' - there's no
    > other way)


    If at this place, the object can only be a B, why would A need to care for
    the destruction? Why would you need polymorphism at all?

    > delete &(static_cast<B*>(this)->x);


    You must never give a pointer to delete that you didn't get from new (except
    a null pointer).

    > }
    >
    > int
    > main(int, char**)
    > {
    > A* a = new B;


    Why do you use an A*? Above, you say that all objects are of type B. So just
    use a B*.

    > cerr << "before\n";
    > // somewhere, there might be someone deleting 'a'
    > delete a;
    > cerr << "after\n";
    > }
    Rolf Magnus, Sep 18, 2005
    #8
  9. "frs" <> wrote in message
    news:...
    > For memory economization, I need to get rid if the virtual
    > destructor.


    You can in fact gain a miniscule amount of time by making the destructor
    non-virtual, a time which probably doesn't make the application fast; but
    you cannot make the application use less memory by doing that.

    Actually, you may be able to shave off one function pointer from the
    application :)

    > Under this constraint I get caught up in a design,
    > where I need to call a destructor of a derived class from a
    > base class.


    You can't call the destructor of the derived class from the base class,
    because the derived object doesn't exist anymore at the time the base class
    destructor is called.

    When the destructor of the base class is called, all of the parts of the
    derived are already dead. You can't call the derived object's destructor and
    expect meaningful things to happen.

    > struct TypeX { ~TypeX() { cerr << "destructed\n"; }
    > struct A { ~A(); }
    > struct B : public A {
    > TypeX x;
    > ~B();
    > }
    >
    > A::~A() {
    > // at this point, ~A knows, that 'this' points is in fact to a 'B'
    > // (it does! no need to say 'type-switches are bad' - there's no
    > other way)


    Your exclamation mark above suggests that you don't completely understand
    object lifetimes. In the destructor above, this points to an A and an A
    alone. There is no B left at that time.

    > delete &(static_cast<B*>(this)->x);


    Undefined behavior. If ~A is being called through the destruction of a B
    object, the destructor of B is already called and is about to complete. You
    cannot call it again.

    Ali
    =?utf-8?Q?Ali_=C3=87ehreli?=, Sep 18, 2005
    #9
  10. frs

    Old Wolf Guest

    John Harrison wrote:
    > John Harrison wrote:
    >> frs wrote:
    >>
    >>> For memory economization, I need to get rid if the virtual
    >>> destructor. Under this constraint I get caught up in a design,
    >>> where I need to call a destructor of a derived class from a
    >>> base class. Briefly it could be displayed like the code below.
    >>> It ends up deleting the member 'x' of 'B' twice. Why? Is there
    >>> a way around?

    >
    > Soory, didn't check that the above compiled. Here's what I really meant.
    >
    > #include<iostream>
    > using namespace std;
    >
    > struct TypeX { ~TypeX() { cerr << "destructed\n"; } };
    > struct A { ~A(); };
    > struct B : public A {
    > TypeX x;
    > ~B();
    > };
    >
    > A::~A() {
    > static_cast<B*>(this)->x.~TypeX();
    > }


    This rather avoids the issue; the B's destructor is never called
    (and the memory for the B is never released).

    Since the OP does not wish to use polymorphishm, maybe he could
    make an A member variable of B, rather than using inheritance.
    Then the destructor will work correctly.
    Old Wolf, Sep 19, 2005
    #10
  11. frs

    red floyd Guest

    Old Wolf wrote:
    > John Harrison wrote:
    >
    >>John Harrison wrote:
    >>
    >>>frs wrote:
    >>>
    >>>
    >>>>For memory economization, I need to get rid if the virtual
    >>>>destructor. Under this constraint I get caught up in a design,
    >>>>where I need to call a destructor of a derived class from a
    >>>>base class. Briefly it could be displayed like the code below.
    >>>>It ends up deleting the member 'x' of 'B' twice. Why? Is there
    >>>>a way around?

    >>
    >>Soory, didn't check that the above compiled. Here's what I really meant.
    >>
    >>#include<iostream>
    >>using namespace std;
    >>
    >>struct TypeX { ~TypeX() { cerr << "destructed\n"; } };
    >>struct A { ~A(); };
    >>struct B : public A {
    >> TypeX x;
    >> ~B();
    >>};
    >>
    >>A::~A() {
    >> static_cast<B*>(this)->x.~TypeX();
    >>}

    >
    >
    > This rather avoids the issue; the B's destructor is never called
    > (and the memory for the B is never released).
    >
    > Since the OP does not wish to use polymorphishm, maybe he could
    > make an A member variable of B, rather than using inheritance.
    > Then the destructor will work correctly.
    >


    Of course:

    void f()
    {
    A a;
    }

    leads to UB.
    red floyd, Sep 19, 2005
    #11
  12. Old Wolf wrote:
    > John Harrison wrote:
    >
    >>John Harrison wrote:
    >>
    >>>frs wrote:
    >>>
    >>>
    >>>>For memory economization, I need to get rid if the virtual
    >>>>destructor. Under this constraint I get caught up in a design,
    >>>>where I need to call a destructor of a derived class from a
    >>>>base class. Briefly it could be displayed like the code below.
    >>>>It ends up deleting the member 'x' of 'B' twice. Why? Is there
    >>>>a way around?

    >>
    >>Soory, didn't check that the above compiled. Here's what I really meant.
    >>
    >>#include<iostream>
    >>using namespace std;
    >>
    >>struct TypeX { ~TypeX() { cerr << "destructed\n"; } };
    >>struct A { ~A(); };
    >>struct B : public A {
    >> TypeX x;
    >> ~B();
    >>};
    >>
    >>A::~A() {
    >> static_cast<B*>(this)->x.~TypeX();
    >>}

    >
    >
    > This rather avoids the issue; the B's destructor is never called
    > (and the memory for the B is never released).
    >


    Well I think that's the point and whether the memory is released or not
    depends on the OP's implementation of delete.

    I'm not advising the above code, it may well be UB for all I know. I'm
    just trying to answer the OP's question.

    john
    John Harrison, Sep 19, 2005
    #12
  13. frs

    frs Guest

    If I am not totally mistaken, then

    delete a;

    does not call the (non-virtual) destructor of class B, because 'a'
    was defined as "A* a = new B;".

    So the destructor of B is never called.
    frs, Sep 19, 2005
    #13
  14. "frs" <> wrote in message
    news:...
    > If I am not totally mistaken, then
    >
    > delete a;
    >
    > does not call the (non-virtual) destructor of class B, because 'a'
    > was defined as "A* a = new B;".
    >
    > So the destructor of B is never called.
    >


    Yes, stepping through the code showed that only the destructor of TypeX and
    A was called... Though is it really the destructor of A or the destructor of
    B? In any case it seems that delete is being called twice on the same
    object, once by the explicit delete in A's constructor and once by an
    implicit delete in A's destructor(atleast this is what stepping through the
    code shows). I don't know why A's destructor would try to delete x but it
    does. Its as if A's destructor is really B. I don't know though. All I do
    know is that you should never delete something that wasn't allocated with
    new.

    Jon
    Jon Slaughter, Sep 19, 2005
    #14
  15. frs

    frs Guest

    Your last phrase, probably hits the point. I guess that x was somehow
    allocated in the frame of the 'newish' 'B' allocation. Deleting x would
    not
    ensure that everything is cleaned up propperly, even though it is
    the only member of B.

    >From the responses I got, I guess, this problem is a tough one. I kept

    the virtual destructor and labeled the issue as 'premature
    optimization.'
    Before, I have a clear idea about the behind the scene handling of
    new and delete, this design is way to daring.

    thanks anyway.
    frs, Sep 20, 2005
    #15
  16. frs

    frs Guest

    The code strip is a heavily cut down of reality, of course.
    'B' is one of many derived classes of A. 'A' knows about 'B' though
    some sort of 'type_id' (category id).

    The content of those classes does not exceed a few bytes themselves.
    Thus, if I have thousands of those little objects, memory increases
    just by the existance of virtual destructors by 30% to 50 %.
    frs, Sep 20, 2005
    #16
  17. "frs" <> wrote in message
    news:...
    > The code strip is a heavily cut down of reality, of course.
    > 'B' is one of many derived classes of A. 'A' knows about 'B' though
    > some sort of 'type_id' (category id).
    >
    > The content of those classes does not exceed a few bytes themselves.
    > Thus, if I have thousands of those little objects, memory increases
    > just by the existance of virtual destructors by 30% to 50 %.
    >


    There is a class that you can use to handle small object allocation by the
    guy who wrote Modern C++ design. In the book he also teaches you how to
    create the class and if you read it you might be able to modify/use it for
    your purpose.

    Jon
    Jon Slaughter, Sep 20, 2005
    #17
  18. frs

    Rolf Magnus Guest

    frs wrote:

    > The code strip is a heavily cut down of reality, of course.
    > 'B' is one of many derived classes of A. 'A' knows about 'B' though
    > some sort of 'type_id' (category id).


    Why don't you want to use C++'s built-in polymorphism instead of some
    home-grown solution?

    > The content of those classes does not exceed a few bytes themselves.
    > Thus, if I have thousands of those little objects, memory increases
    > just by the existance of virtual destructors by 30% to 50 %.


    A virtual destructor adds one pointer per class, not per object. Or is the
    destructor the only virtual member?
    On most - if not all - implementations, making a class polymorphic (i.e.
    giving it virtual member functions) will add the size of one pointer per
    object. If you add more virtual member funcitons, the object size will not
    increase further. It only costs the size of one function pointer per class
    and function.
    Rolf Magnus, Sep 20, 2005
    #18
  19. frs

    frs Guest

    Thanks,

    but I was was indeed looking for avoidance of virtual functions at all.
    So,
    no pointer to a virtual function table would be necessary.
    frs, Sep 21, 2005
    #19
  20. frs

    Rolf Magnus Guest

    frs wrote:

    > Thanks,
    >
    > but I was was indeed looking for avoidance of virtual functions at all.
    > So, no pointer to a virtual function table would be necessary.


    I see. However, if you implement your own replacement for them, you need to
    store type information too. The code is likely to be slower than the
    virtual functions in C++, so you should be sure that the additional work
    actually pays off.
    Rolf Magnus, Sep 21, 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. Honne Gowda A
    Replies:
    2
    Views:
    873
    Karl Heinz Buchegger
    Oct 31, 2003
  2. J.T. Conklin
    Replies:
    1
    Views:
    440
    David Hilsee
    Aug 11, 2004
  3. qazmlp
    Replies:
    1
    Views:
    567
    qazmlp
    Apr 10, 2005
  4. LAvoisieR
    Replies:
    8
    Views:
    431
    LAvoisieR
    Oct 27, 2005
  5. Taran
    Replies:
    6
    Views:
    381
    Default User
    Apr 20, 2006
Loading...

Share This Page