Topic: address of operator delete


Author: Chelly Green <chelly@eden.com>
Date: 1996/10/21
Raw View
Steve Clamage wrote:
...
> When you delete a polymorphic object, for example
>         class X { ... virtual ~X(); ... };
>         extern X* x;
>         delete x;
> the runtime system can tell from the virtual destructor that gets called
> what the type of the actual object is, and the correct version of operator
> delete gets called automatically. **  (If X::~X() is not virtual and if x
> points to an object derived from X, the results are undefined.)
>
> When you take the address of x->operator delete, you get the same sort
> of result you would get for any static member function: the address
> of X::operator delete.
>
> ** A simple way to implement this functionality: Each destructor
> conditionally calls the operator delete appropriate for its own class,
> depending on a hidden flag passed to the destructor. The compiler
> generates code to set that flag for the most-derived destructor, and
> that destructor passes an unset flag to the base-class destructors
> that it calls. (Remember that a destructor first calls each of its
> own immediate base-class destructors, then executes its own body.)

That's the (inefficient) way the compiler I use implements it. It adds
extra overhead that isn't needed:

    struct A {
        virtual ~A();
        void* operator new ( size_t );
        void operator delete ( void* );
    };

    struct B : A {
        virtual ~B();
        void* operator new ( size_t );
        void operator delete ( void* );
    };

You are suggesting this, which is perfectly OK, except for efficiency
(what the compiler generates, not user code, of course!):

    A::~A( bool call_delete )
    {
        // user code
        if ( call_delete ) operator delete ( this );
    }

    B::~B( bool call_delete )
    {
        // user code
        A::~A( false );
        if ( call_delete ) operator delete ( this );
    }

    void f( A* a )
    {
        // delete a;
        a->~A( true );
    }

But as you said, "a simple way to implement it". I suggest this, which
is also simple:

    A::~A()
    {
        // user code
    }

    A::~A_delete()
    {
        A::~A();
        operator delete( this );
    }

    B::~B()
    {
        // user code
        A::~A();
    }

    B::~B_delete()
    {
        B::~B();
        operator delete( this );
    }

    void f( A* a )
    {
        // delete a;
        a->~A_delete();
    }

Add in virtual bases, checking for a NULL this pointer in the
destructor, and the separate function solution works well. It also
applies to construction when it needs to know if the virtual base needs
to be constructed (which is done only in the most-derived class). If an
object is never deleted (all on the stack), then the extra destructor
can be linked out of the executable.

Just an efficiency concern. Next time, how to move the thunk code for
'this' pointer adjustment into the vtable itself...  :-)
--
Chelly Green | chelly@eden.com | C++ - http://www.eden.com/~chelly
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Kent Budge <"kgbudge,"@@uunet.uu.net>, sandia@uunet.uu.net,
Date: 1996/10/22
Raw View
David Sachs wrote:
...
>
> I was afraid of this. The problem is that, even though it is a static,
> function, operator delete behaves as if it were virtual, when it is
> invoked via a delete expression.
>
...

I think you lost me here.  In what sense is operator delete virtual?
The destructor invoked in a delete expression may certainly be virtual,
but that's a different matter (and a different function.)  By the time
the actual delete operator is called, the memory pointed is already
"destructed," is no longer an object, and is presumably ready to return
to the heap as raw memory.

--
Kent G. Budge
kgbudge@sandia.gov
(usual disclaimer)
Return address hacked to foil junk mail; edit before replying.
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: jpotter@falcon.lhup.edu (John E. Potter)
Date: 1996/10/22
Raw View
Steve Clamage (clamage@taumet.eng.sun.com) wrote:
: In article MAA23382@taumet.eng.sun.com, clamage@taumet.eng.sun.com
: (Steve Clamage) writes:

: >
: >** A simple way to implement this functionality: Each destructor
: >conditionally calls the operator delete appropriate for its own class,
: >depending on a hidden flag passed to the destructor. The compiler
: >generates code to set that flag for the most-derived destructor, and
: >that destructor passes an unset flag to the base-class destructors
: >that it calls. (Remember that a destructor first calls each of its
: >own immediate base-class destructors, then executes its own body.)

: Oops. I got the last sentence backwards, as Marco Dalla Gasperina was
: the first to point out. The compiler generates code to execute the
: user-written destructor body, then calls the destructor for each
: immediate base class in reverse order of construction.

However, as you were in implementer mode not user mode, it is true that
the destructor, user body, is executed first, followed by member destructors,
followed by immediate base-class destructors, THEN the remainder of
the implementation generated body which could include calling operator
delete.  So, your remember is valid following execution of the user
written destructor body.

My appologies for being one of those who pointed out the oops(not) which
was simply a matter of mode.

John
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: clamage@taumet.Eng.Sun.COM (Steve Clamage)
Date: 1996/10/22
Raw View
In article 23F3@eden.com, Chelly Green <chelly@eden.com> writes:
>Steve Clamage wrote:
>....
>> ** A simple way to implement this functionality: Each destructor
>> conditionally calls the operator delete appropriate for its own class,
>> depending on a hidden flag passed to the destructor. The compiler
>> generates code to set that flag for the most-derived destructor, and
>> that destructor passes an unset flag to the base-class destructors
>> that it calls.
>
>That's the (inefficient) way the compiler I use implements it. It adds
>extra overhead that isn't needed:
>
>    struct A {
>        virtual ~A();
>        void* operator new ( size_t );
>        void operator delete ( void* );
>    };
>
>    struct B : A {
>        virtual ~B();
>        void* operator new ( size_t );
>        void operator delete ( void* );
>    };
>
>You are suggesting this, which is perfectly OK, except for efficiency
>(what the compiler generates, not user code, of course!):
>
>    A::~A( bool call_delete )
>    {
>        // user code
>        if ( call_delete ) operator delete ( this );
>    }
>
>    B::~B( bool call_delete )
>    {
>        // user code
>        A::~A( false );
>        if ( call_delete ) operator delete ( this );
>    }
>
>    void f( A* a )
>    {
>        // delete a;
>        a->~A( true );
>    }
>
>But as you said, "a simple way to implement it". I suggest this, which
>is also simple:
>
>    A::~A()
>    {
>        // user code
>    }
>
>    A::~A_delete()
>    {
>        A::~A();
>        operator delete( this );
>    }
>
>    B::~B()
>    {
>        // user code
>        A::~A();
>    }
>
>    B::~B_delete()
>    {
>        B::~B();
>        operator delete( this );
>    }
>
>    void f( A* a )
>    {
>        // delete a;
>        a->~A_delete();
>    }

The first example implementation requires an extra test and jump in every
destructor. The second example eliminates the extra test and jump, but
creates twice as many destructors. The number of machine cycles to execute
the code is smaller in the second example, but the total code size is larger,
along with the symbol table.

Which example is more "efficient"? That depends on whether you care more
about code size or code speed when you must choose one or the other. It
depends on whether the extra time of the first example (or the extra space
of the second example) represents a measurable difference in the complete
program. Finally, a larger program might result in more page faults and
much longer overall execution time than a "less efficient" smaller program.

"Function splitting", the usual term for the second example, is often a
good solution, particularly in environments where the program size is not
a major consideration. I just wanted to point out that size vs speed is
long-standing dichotomy in computer programming, and that often neither
one can be preselected as "better".

---
Steve Clamage, stephen.clamage@eng.sun.com
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: jpotter@falcon.lhup.edu (John E. Potter)
Date: 1996/10/23
Raw View
Steve Clamage (clamage@taumet.eng.sun.com) wrote:
: (Remember that a destructor first calls each of its
: own immediate base-class destructors, then executes its own body.)

Ah, isn't it a constructor first calls it base-class constructors,
left to right, then members top to bottom, then body, while a
destructor first does it body, then members bottom to top, then
base-class destructors, right to left?

John
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: clamage@taumet.eng.sun.com (Steve Clamage)
Date: 1996/10/18
Raw View
In article m00@fsgi02.fnal.gov, b91926@fsgi02.fnal.gov (David Sachs) writes:
>If x is a pointer to a polymorphic class, what is the value
>of x->operator delete  (operator delete for the base class
>corresponding to the type of x or operaor delete for the class
>of the actual object pointed to by x)

Member functions operator new and operator delete are static functions,
whether they are declared static or not. (The must be static, because
at the time they are called they get the address of raw memory, not
an object. The object is either not yet constructed or already destroyed.)

When you delete a polymorphic object, for example
 class X { ... virtual ~X(); ... };
 extern X* x;
 delete x;
the runtime system can tell from the virtual destructor that gets called
what the type of the actual object is, and the correct version of operator
delete gets called automatically. **  (If X::~X() is not virtual and if x
points to an object derived from X, the results are undefined.)

When you take the address of x->operator delete, you get the same sort
of result you would get for any static member function: the address
of X::operator delete.

** A simple way to implement this functionality: Each destructor
conditionally calls the operator delete appropriate for its own class,
depending on a hidden flag passed to the destructor. The compiler
generates code to set that flag for the most-derived destructor, and
that destructor passes an unset flag to the base-class destructors
that it calls. (Remember that a destructor first calls each of its
own immediate base-class destructors, then executes its own body.)
---
Steve Clamage, stephen.clamage@eng.sun.com




[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: kanze@gabi-soft.fr (J. Kanze)
Date: 1996/10/18
Raw View
b91926@fsgi02.fnal.gov (David Sachs) writes:

> If x is a pointer to a polymorphic class, what is the value
> of x->operator delete  (operator delete for the base class
> corresponding to the type of x or operaor delete for the class
> of the actual object pointed to by x)

Operator delete for the static type of x.  Operator delete is a static
function, and thus, cannot be virtual.

--
James Kanze           (+33) 88 14 49 00          email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs Bourgeois, 67000 Strasbourg, France
Conseils en informatique industrielle --
                            -- Beratung in industrieller Datenverarbeitung


[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: clamage@taumet.eng.sun.com (Steve Clamage)
Date: 1996/10/18
Raw View
In article MAA23382@taumet.eng.sun.com, clamage@taumet.eng.sun.com (Steve Clamage) writes:

>
>** A simple way to implement this functionality: Each destructor
>conditionally calls the operator delete appropriate for its own class,
>depending on a hidden flag passed to the destructor. The compiler
>generates code to set that flag for the most-derived destructor, and
>that destructor passes an unset flag to the base-class destructors
>that it calls. (Remember that a destructor first calls each of its
>own immediate base-class destructors, then executes its own body.)

Oops. I got the last sentence backwards, as Marco Dalla Gasperina was
the first to point out. The compiler generates code to execute the
user-written destructor body, then calls the destructor for each
immediate base class in reverse order of construction.

---
Steve Clamage, stephen.clamage@eng.sun.com


[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: b91926@fsgi02.fnal.gov (David Sachs)
Date: 1996/10/19
Raw View
kanze@gabi-soft.fr (J. Kanze) writes:

>b91926@fsgi02.fnal.gov (David Sachs) writes:

>> If x is a pointer to a polymorphic class, what is the value
>> of x->operator delete  (operator delete for the base class
>> corresponding to the type of x or operaor delete for the class
>> of the actual object pointed to by x)

>Operator delete for the static type of x.  Operator delete is a static
>function, and thus, cannot be virtual.

>--
>James Kanze           (+33) 88 14 49 00          email: kanze@gabi-soft.fr
>GABI Software, Sarl., 8 rue des Francs Bourgeois, 67000 Strasbourg, France
>Conseils en informatique industrielle --
>                            -- Beratung in industrieller Datenverarbeitung

I was afraid of this. The problem is that, even though it is a static, function,
operator delete behaves as if it were virtual, when it is invoked via a delete
expression.

In view of the lack of a placement delete expression in the proposed C++
standard, the question remains as to what code could be used to reasonably
simulate a placement delete. Clearly the following snippet will NOT work.

void delete_obj(some_class *x, some_type v)
{
  x->~some_class();
  x->operator delete(x,v);
}

I suspect that there really is no way to simulate the hypothetical
non-existant placement delete expression:  delete(v) x;
--
** The Klingons' favorite food was named by the first earthling to see it **
David Sachs - Fermilab, MSSG MS369 - P. O. Box 500 - Batavia, IL 60510
Voice: 1 708 840 3942      Deparment Fax: 1 708 840 3785


[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: b91926@fsgi02.fnal.gov (David Sachs)
Date: 1996/10/16
Raw View
If x is a pointer to a polymorphic class, what is the value
of x->operator delete  (operator delete for the base class
corresponding to the type of x or operaor delete for the class
of the actual object pointed to by x)
--
** The Klingons' favorite food was named by the first earthling to see it **
David Sachs - Fermilab, MSSG MS369 - P. O. Box 500 - Batavia, IL 60510
Voice: 1 708 840 3942      Deparment Fax: 1 708 840 3785
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]