Re: overloading new/delete operator
jstach...@poczta.fm wrote:
I have overloaded new and delete (here is the full post):
No you haven't. You've replaced the global new and delete
operators. But with something that isn't legal, giving
undefined behavior.
Code Block 1
<code>
inline void* operator new(size_t size) throw() {
return TC::MemoryPoolManager::getInstance().allocateMemory(size);
}
inline void operator delete(void* deletable, size_t size) throw() {
TC::MemoryPoolManager::getInstance().releaseMemory(deletable,
size);
}
inline void* operator new [](size_t size) throw() {
return TC::MemoryPoolManager::getInstance().allocateMemory(size);
}
inline void operator delete[](void * deletable, size_t size) throw() {
TC::MemoryPoolManager::getInstance().releaseMemory(deletable,
size);
}
</code>
The global new and delete operators (at least the standard
forms) are not allowed to be defined inline.
new operator is called without any problems, eg.:
Code Block 2
<code>
template<typename T>
T*** NMap<T>::alloc3DArray(int c, int sx, int sy) {
T*** data = new T**[c];
mem_begin_ = new T[c*sx*sy];
T* tmp;
for (int i = 0; i < c; i++) {
tmp = (mem_begin_ + i*sx*sy);
data[i] = new T*[sy];
for (int y = 0; y < sy; y++) {
data[i][y] = (tmp + y*sx);
}
}
allocated++;
return data;
}
</code>
but the delete operator is called from somewhere else (it is not
called the one that is defined by me [Code block 1]):
Code Block
<code>
template<typename T>
void NMap<T>::free3DArray(T*** data, int c) {
for (int i = 0; i < c; i++) {
T** d = data[i]; //some external delete
delete[] d;
}
delete[] data;
delete[] mem_begin_;
deallocated++;
}
</code>
where is the problem ???
Well, first, you have undefined behavior, because you've defined
your replacement new and delete operators inline. Then, you go
out of your way to ensure that the undefined behavior will cause
problems, by using templates, so you don't know where what will
be allocated or deleted.
The question is what you really want to do:
-- If you really want to replace the global new and delete, it
should be sufficient to put your functions in a source file,
compile it, and link it in before the standard C++ library.
(The standard doesn't say this---it doesn't say anything
about *how* you compile and link. But this works for all
compilers I've ever seen: Sun CC, g++, xlC, VC++...).
Generally, however, this is a bad idea, except for global
debugging versions of the operators.
-- If you only want certain classes (or templates) to use your
operators, make them members of the class.
-- If you only want them used in the functions above, give them
different names, and separate allocation from
initialization, like the STL does, i.e.: instead of
p = new T ;
use
p = static_cast< T* >( allocate( sizeof( T ) ) ) ;
new ( p ) T ;
and instead of:
delete p ;
use
p->~T() ;
free( p ) ;
(Obviously, you don't want to do this generally, or require
client code to do it. But it works well when localized in a
specific class or template.)
-- If you want to offer a choice at the allocation point, use
placement new, i.e. define a new operator which takes an
additional argument telling you how to allocate.
Note that if you do this, you will have to replace the
global new and delete as well: since the delete expression
does not call your placement delete, you have to somehow
save the information which new was used, and exploit it in
the global delete to choose which delete to use.
I use in my project some static libraries and one of them contains
definitions of new/delete operators:
Code Block
<code>
// Custom memory allocator
namespace nv {
namespace mem {
NVCORE_API void * malloc(size_t size);
NVCORE_API void * malloc(size_t size, const char * file, int
line);
NVCORE_API void free(const void * ptr);
NVCORE_API void * realloc(void * ptr, size_t size);
} // mem namespace
} // nv namespace
// Override new/delete
inline void * operator new (size_t size) throw() {
return nv::mem::malloc(size);
}
inline void operator delete (void *p) throw() {
nv::mem::free(p);
}
inline void * operator new [] (size_t size) throw() {
return nv::mem::malloc(size);
}
inline void operator delete [] (void * p) throw() {
nv::mem::free(p);
}
</code>
Again, this is illegal, and results in undefined behavior. If
you wrote the libraries, correct them. If you didn't, don't use
them; they cannot be made to work correctly, except by chance.
When I put in my code non-inline version of new and delete:
Code Block
<code>
void* operator new(size_t size) throw() {
return TC::MemoryPoolManager::getInstance().allocateMemory(size);
}
void operator delete(void* deletable, size_t size) throw() {
TC::MemoryPoolManager::getInstance().releaseMemory(deletable,
size);
}
void* operator new [](size_t size) throw() {
return TC::MemoryPoolManager::getInstance().allocateMemory(size);
}
void operator delete[](void * deletable, size_t size) throw() {
TC::MemoryPoolManager::getInstance().releaseMemory(deletable,
size);
}
</code>
I got following linker errors (they don't appear if I precede
my new/ delete definitions by inline keyword - but it still
does not work):
You have undefined behavior as soon as any standard operator new
or operator delete is declared inline. Anything can happen.
[...]
How can I disable the new/delete operators from static libraries ??
Don't provide a standard global new/delete operator in a
library. Ever. A library which requires a non-standard version
of operator new or operator delete is simply unusable. Period.
--
James Kanze (GABI Software) email:james.kanze@gmail.com
Conseils en informatique orient=E9e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France, +33 (0)1 30 23 00 34