 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
albrecht.fritzsche Guest
|
Posted: Sat Aug 26, 2006 9:40 pm Post subject: Overloading operator delete problem |
|
|
Hi,
I've overloaded operator new and operator delete like in the appended
code but don't know how to trigger a call to the nothrow-delete.
While the call to the nothrow-new is easy by specifying
int* p = new(nothrow) int;
how do I call the nothrow-delete
delete (nothrow) p;
does not work.
At least on Vis C++ the appended code compiles but uses for the last
delete the throwing-delete version. How do I have to specify the
std::nothrow? Where is this actually documented in the standard?
And, why do I get a strange error from Comeau (online) with exactly
this code saying
| Quote: | "ComeauTest.c", line 6: error: exception specification is incompatible with that of
previous function "operator delete(void *)" (declared at line 67 of
"new.stdh"):
previously specified: no exceptions will be thrown
void operator delete(void* memory) throw(std::bad_alloc) {
^
|
Thanks for any help finding what I am doing wrong
Ali
#include <new>
#include <cstdlib>
void* operator new(size_t count) throw (std::bad_alloc) {
return malloc(count);
}
void operator delete(void* memory) throw(std::bad_alloc) {
free(memory);
}
void* operator new(size_t count, const std::nothrow_t&) throw () {
return malloc(count);
}
void operator delete(void* memory, const std::nothrow_t&) throw() {
free(memory);
}
int main() {
int* i = new int();
int* j = new(std::nothrow) int();
delete i;
delete /*(std::nothrow)*/ j; // how to specify the nothrow?
return 0;
}
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
Woojong Koh Guest
|
Posted: Mon Aug 28, 2006 8:27 am Post subject: Re: Overloading operator delete problem |
|
|
albrecht.fritzsche wrote:
| Quote: | how do I call the nothrow-delete
delete (nothrow) p;
does not work.
|
Hi,
What you want is simply calling the delete of no-throw version,
operator delete(p, std::nothrow);
would be an answer for your question.
But if you want to *overload* delete functions under the Visual C++
environment, it could be more trciky. 'cause the IDE has usually its own
delete functions.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
kanze Guest
|
Posted: Mon Aug 28, 2006 6:01 pm Post subject: Re: Overloading operator delete problem |
|
|
albrecht.fritzsche wrote:
| Quote: | I've overloaded operator new and operator delete like in the
appended code but don't know how to trigger a call to the
nothrow-delete.
|
Throw an exception from a constructor called by new(nothrow).
(BTW: I think you mean you've replaced the standard functions.
Overloading would imply that you defined additional functions,
with different parameters.)
| Quote: | While the call to the nothrow-new is easy by specifying
int* p = new(nothrow) int;
how do I call the nothrow-delete
delete (nothrow) p;
does not work.
|
You don't call it, the compiler does. And only if you exit the
constructor by an exception in a new(nothrow).
Normally, too, you wouldn't replace new(nothrow) without also
replacing the standard new and delete. For the simple reason
that memory allocated with new(nothrow) will be freed with the
standard delete.
| Quote: | At least on Vis C++ the appended code compiles but uses for
the last delete the throwing-delete version.
|
The non-placement delete, as it should. All deletes should be
non-throwing.
| Quote: | How do I have to specify the std::nothrow? Where is this
actually documented in the standard?
|
In §18.4 and §5.3.4.
| Quote: | And, why do I get a strange error from Comeau (online) with
exactly this code saying
"ComeauTest.c", line 6: error: exception specification is incompatible
with that of
previous function "operator delete(void *)" (declared at line
67 of
"new.stdh"):
previously specified: no exceptions will be thrown
void operator delete(void* memory) throw(std::bad_alloc) {
|
Because delete must have an exception specifier throw(). It is
not allowed to throw an exception.
| Quote: | #include <new
#include <cstdlib
void* operator new(size_t count) throw (std::bad_alloc) {
return malloc(count);
|
This is illegal. You've violated the contract. You have to do
something like:
void* result = malloc( count ) ;
if ( result == NULL ) {
throw std::bad_alloc() ;
}
return result ;
In a more general version, you should probably check if the
new_handler has been set as well. (But I often skip this; any
global operator new that I replace will be specific to the
application, and if I know that the application never sets the
new_handler, I don't have to worry about it.)
| Quote: | }
void operator delete(void* memory) throw(std::bad_alloc) {
|
Should be:
void operator delete( void* memory ) throw() {
| Quote: | free(memory);
}
void* operator new(size_t count, const std::nothrow_t&) throw () {
return malloc(count);
}
void operator delete(void* memory, const std::nothrow_t&) throw() {
free(memory);
}
int main() {
int* i = new int();
int* j = new(std::nothrow) int();
delete i;
delete /*(std::nothrow)*/ j; // how to specify the nothrow?
|
You can't, here.
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
Jens Theisen Guest
|
Posted: Tue Aug 29, 2006 2:55 am Post subject: Re: Overloading operator delete problem |
|
|
kanze wrote:
| Quote: | The non-placement delete, as it should. All deletes should be
non-throwing.
|
Naturally enough, but can you tell me why there is a separate
nothrow-delete then?
Cheers,
Jens
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
kanze Guest
|
Posted: Tue Aug 29, 2006 8:22 pm Post subject: Re: Overloading operator delete problem |
|
|
Jens Theisen wrote:
| Quote: | kanze wrote:
The non-placement delete, as it should. All deletes should be
non-throwing.
Naturally enough, but can you tell me why there is a separate
nothrow-delete then?
|
So the compiler knows to call it if the constructor of a
nothrow new exits by an exception.
The problem is simple. Suppose I write something like:
MyClass* p = new MyClass ;
and the constructor of MyClass throws an exception. The memory
for MyClass has already been allocated (by the function `operator
new( size_t )'), but the user code has no pointer to it. So it
is up to the compiler to clean it up, by calling `operator
delete( void* )'.
Now consider what happens in the case of placement new. The
most common placement new (the only standard one in
pre-exception days) is new(void*), which is used to explicitly
call the constructor, without allocating memory. (Formally, it
"allocates" by calling the allocator function `operator new(
size_t, void* p )', which does nothing but return p.) Something
like:
// With suitable precautions for alignment...
char buffer[ size ] ;
new ( buffer ) MyClass ;
Again, what happens if the constructor of MyClass throws?
Calling `operator delete( void* )' with the address (buffer) is
obviously not a very good idea.
The first proposal was to call operator delete() for
non-placement new, and nothing for placement new. But there are
many forms of placement new---the user can define just about
anything he wants. And some of them, like new(nothrow) DO
require delete to be called.
The result was the introduction of the placement delete
functions. They cannot be invoked from a delete expression;
they can be invoke by means of `operator delete(...)', but
that's obviously not the intent. They will be invoked if the
constructor in a placement new fails, however; their presence or
absence determines whether the compiler invokes an operator
delete function or not in placement new. And since in a
new(nothrow), we definitely have to invoke a delete if the
constructor fails, there is an operator delete(void*, nothrow)
function.
Similarly: if the operator new() function being used is declared
throw(), the compiler assumes that it reports a lack of memory
by returning a null pointer, and tests for null before calling
the constructor; if the operator new() function can raise an
exception, the compiler supposes that it will never return null.
You might want to try the following program; it should show the
behavior fairly well:
#include <iostream>
#include <ostream>
#include <memory>
#include <stdlib.h>
#define PASTE(a,b) a ## b
#define UNIQ(a) PASTE(a,__LINE__)
#define TRACE( fn ) \
do { \
static bool UNIQ(nested) = false ; \
if ( ! UNIQ(nested) ) { \
UNIQ(nested) = true ; \
std::cout << fn << std::endl ; \
UNIQ(nested) = false ; \
} \
} while ( false )
void*
operator new( size_t n ) throw( std::bad_alloc )
{
TRACE( "new()" ) ;
void* result = malloc( n ) ;
if ( result == NULL ) {
throw std::bad_alloc() ;
}
return result ;
}
void
operator delete( void* p ) throw()
{
TRACE( "delete()" ) ;
free( p ) ;
}
void*
operator new( size_t n, std::nothrow_t const& ) throw()
{
TRACE( "new(nothrow)" ) ;
return malloc( n ) ;
}
void
operator delete( void* p, std::nothrow_t const& ) throw()
{
TRACE( "delete(nothrow)" ) ;
free( p ) ;
}
void*
operator new( size_t n, double ) throw()
{
TRACE( "new(double)" ) ;
return malloc( n ) ;
}
struct MyClass
{
MyClass( bool b ) { if ( b ) throw 42 ; }
} ;
template< typename T >
void
test( T placement )
{
MyClass* p = new( placement ) MyClass( false ) ;
delete p ;
try {
p = new( placement ) MyClass( true ) ;
delete p ;
} catch ( ... ) {
}
}
void
test()
{
MyClass* p = new MyClass( false ) ;
delete p ;
try {
p = new MyClass( true ) ;
delete p ;
} catch ( ... ) {
}
}
int
main()
{
test() ;
test( std::nothrow ) ;
test( 3.14159 ) ;
}
You'll notice that in the case of new(double), it leaks memory
when MyClass::MyClass() throws. If the pointer returned from
new(double) didn't actually allocate memory, this would be a
feature, and not a defect.
Also note that because the non-placement delete is called for
normal deletes, if you provide a special placement new of your
own, you also have to replace the non-placement operator delete,
AND the non-placement operator new, ensuring that in all of the
operator new's, you do something so that the non-placement
operator delete can tell how to dispose of the memory.
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
kanze Guest
|
Posted: Tue Aug 29, 2006 11:50 pm Post subject: Re: Overloading operator delete problem |
|
|
Greg Herlihy wrote:
| Quote: | albrecht.fritzsche wrote:
|
[...]
| Quote: | Of course one may well wonder why a nothrow version of
delete() exists at all - since its behavior seems to be
indistinguishable from the non-nothrow version.
|
Because of the behavior if the constructor in a new expression
throws. Basically, the compiler looks for a matching delete.
If it finds one, it calls it; if not, the memory is not deleted.
The normal delete is not a match for a placement delete, and
without the nothrow delete, you'd have a memory leak in this
case.
A more interesting question is why `operator delete( void*,
void* )' exists. The function is required to do nothing; it is
only called if the constructor of a placement new exits by an
exception, and if it weren't present, the compiler wouldn't call
any deallocator (which of course does nothing).
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
kanze Guest
|
Posted: Thu Sep 28, 2006 6:06 pm Post subject: Re: Overloading operator delete problem |
|
|
Jens Theisen wrote:
| Quote: | "kanze" <kanze@gabi-soft.fr> writes:
It just occured to me that the classical placement new (as
opposed to the nothrows), doesn't follow that pattern. The
following is obviously illegal, ptr should be destroyed by a
call to ~A.
int main()
{
A *ptr = (A*)malloc(sizeof(A));
ptr = new (ptr) A;
delete ptr;
free(ptr);
}
|
I'm not sure what your trying to say, but the code above is
illegal. You do NOT use the operator delete on a pointer
returned from the classical placement new. You explicitly call
the destructor, i.e. the last two lines above should be:
ptr->~A() ;
free( ptr ) ;
There is a placement form of the operator delete function for
the classical placement new. It does nothing. It will only be
called, however, if the constructor for A throws. (Note that
when separating allocation and initialization in this manner, it
is your responsibility to free the memory if the constructor
throws.)
| Quote: | Also note that because the non-placement delete is called
for normal deletes, if you provide a special placement new
of your own, you also have to replace the non-placement
operator delete, AND the non-placement operator new,
ensuring that in all of the operator new's, you do something
so that the non-placement operator delete can tell how to
dispose of the memory.
Or you provide a custom delete function for your type. Then
you can't delete them with delete, of course, but you also
can't delete with delete what you've allocated by classic
placement new.
So actually we have:
allocated by | deleted by
new delete
new (nothrow) delete
new (void*) ~T
new (custom_t) custom-defined
|
More or less.
The problem is that placement new has several uses. In the case
of new(void*), it's really just a hack to call the constructor,
and not a "new" at all, except formally. In other cases, the
issue is more subtle. When using custom allocators with
placement new, for example, it is usual to take over all of the
memory management, so that the standard delete can be used
everywhere. (I'm not sure, but I think that the standard
implementation of new(nothrow) is required to use plain new, so
that just replacing plain new and plain delete should be
sufficient there.)
If this rule is followed, placement new's fall into two
categories: either you use delete, or you invoke the destructor
directly, with no call to delete. In my experience, the
classical placement new is in fact about the only case of the
latter.
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
Alberto Ganesh Barbati Guest
|
Posted: Thu Sep 28, 2006 10:51 pm Post subject: Re: Overloading operator delete problem |
|
|
kanze ha scritto:
| Quote: | (I'm not sure, but I think that the standard
implementation of new(nothrow) is required to use plain new, so
that just replacing plain new and plain delete should be
sufficient there.)
|
I could not find evidence of such guarantee. The wording just says "This
nothrow version of operator new returns a pointer obtained as if
acquired from the ordinary version." Few lines below the standard
describes the behavior by presenting a precise algorithm, so it seems to
me that default new and new(nothrow) are not intended to depend on one
another. I guess the idea was to allow this kind of implementation:
void* operator new(size_t n)
{
for(;
{
void* ptr = malloc(n);
if(ptr)
return ptr;
if(!_new_handler)
throw bad_alloc();
_new_handler();
}
}
void* operator new(size_t n, nothrow_t) throw()
{
for(;
{
void* ptr = malloc(n);
if(ptr)
return ptr;
if(!_new_handler)
return 0;
try
{
_new_handler();
}
catch(bad_alloc)
{
return 0;
}
}
}
As you can see, the try-block of new(nothrow) is entered only if
allocation fails the first time and the user provided a new-handler. On
platforms where entering a try-block have a performance hit, this could
be better than an implementation based on regular new, which would
always require entering the try-block:
void* operator new(size_t n, nothrow_t) throw()
{
try
{
return ::operator new(n);
}
catch(bad_alloc)
{
return 0;
}
}
This implies, as you pointed out, that each time an application provides
a replacement function for global operator new, it should also provide a
replacement for the nothrow version.
I find this lack of clarity an inconvenient. Maybe the standard should
specify more explicitly the relationship between new and new(nothrow) or
the lack of it. Any ideas?
Ganesh
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|