Re: Implementing overloaded operator new[]/delete[]
On Sep 24, 4:26 pm, "Alf P. Steinbach" <al...@start.no> wrote:
* James Kanze:
[...]
No. Actually, apart from implementing your own per-class allocation
scheme, the main use of implementing your own allocator function that
I'm aware of is to obfuscate the new'ing of an instance of the class=
in
question, so that you're in practice ensured that only your own spec=
ial
macro for that is used, which then guarantees that the pointer produ=
ced
is wrapped in a suitable smart pointer.
That doesn't work.
It works.
Maybe we're talking about different things. I thought I
understood that you defined a macro with the name "new", e.g.:
#define new new( __FILE__, __LINE__ )
That doesn't work; formally, it's undefined behavior, and
practically, it's going to cause no end of havoc as soon as you
include something like <vector> (unless, of course, you're
compiler supports "export", and the library was written with
this in mind).
The macro will look something like
#define NEW( Type, Args ) ungrokkablegoobledygookexpression
OK. But it will only verify memory which uses NEW, and not new.
That is, you can't easily apply it retroactively.
[snip]
How does a debugger tell you what happened in the past? Where a
block was allocated, if it turns out to have leaked, for
example. For that matter, how does it tell you you've
overwritten beyond the end of an allocated block?
The compiler emits debugging meta-information, ordinary
allocations are replaced with operations that keep some
meta-information, memory is initialized to special
bit-patterns, and so on, to help the debugger.
In sum, it's the special new/delete which does the actual work;
you only use the debugger to see the results. It's not that
different from what I do under Unix, except that the program
isn't running under the debugger; the debugger is only used for
the post-mortum (or addr2line in the case of leaks, which don't
produce a core dump).
In short, the compiler + runtime library does the job that can be done
manually, plus things that can't be done manually, in support of
debugging. An example of debug output information (showing the source
code line that allocated some block that wasn't subsequently
deallocated) is shown at <url:http://msdn2.microsoft.com/en-us/library/e5=
ewb1h3(VS.80).aspx>.
However, detecting write-beyond-end-of-block is in general not
feasible, because the granularity of protection offered by an
OS is typically 4K or larger, one page. It can be done (e.g.,
in Windows, using pageheap.exe, which I searched up now, never
used it!), but at the cost of allocating one extra protection
page for each allocation. That particular problem is easier
dealt with at higher levels, e.g. using std::vector::at
instead of operator[], or simply sound design... ;-)
There are two alternatives for detecting
write-beyond-end-of-block. Purify does it by instrumenting the
code; every hardware store instruction is transformed into
something which verifies the boundaries. (I think they may even
have a patent on the technique.) My own debugging allocators
provide a guard zone before and after each initializer,
initialized with a special pattern, and declare an error if that
pattern isn't present when the memory is freed. That doesn't
indicate where the overwrite occured, but once you've got a
stack walkback of where the memory was allocated, and of where
it was freed, it's usually not too difficult to find the error.
(In practice, I've found very little problem with buffer
overruns in C++. It was a common problem with malloc,
however.)
--
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