Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

new and delete woes

2 views
Skip to first unread message

Andy Paterson

unread,
Apr 28, 2004, 3:45:49 PM4/28/04
to
Hello people,
I've gathered the following fact from reading various posts on this
list:
An overloaded delete operator cannot be explicitly called. You can
overload delete, but it's only every called when an equivalently
overloaded new throws an exception.

My question is, are there any clever workarounds for this?

Hopefully, I am missing something important, but as it stands this is
pretty annoying.

Context: I've got multiple DLLs in my project, and I want to
supplement the normal new and delete operators with DLL safe versions,
looking a bit like this:
(note: I'm paraphrasing my code here... expect errors)

struct SDLLSafe { } g_DLLSafe;

void* operator new(size_t NumBytes,const SDLLSafe&)
{
return DLLSafeMalloc(NumBytes);
}

void* operator new[](size_t NumBytes,const SDLLSafe&)
{
return DLLSafeMalloc(NumBytes);
}

int* pDllSafeInt = new (g_DLLSafe) int;

That works fine, but because there is no delete equivalent, I've had
to write something that looks like this:

template <typename T>
void _DLLSafeDelete(T* pPtr)
{
if (pPtr)
{
T* const pLast = pPtr + (_msize(pPtr) / sizeof(T));
for (T* pCur = pPtr; pCur != pLast; (pCur++)->~T());
DLLSafeFree(pPtr);
}
}

So the new/delete looks like:
int* pDllSafeInt = new (g_DLLSafe) int;
_DLLSafeDelete(pDLLSafeInt);

I have some problems with this: It doesn't look nice, it relies on
non-standard memory size checking, and it requires explicit
destruction of the objects in memory (whereas the overloaded new
implicit constructs objects).
Also, surely having an explicitly called overloaded delete would be a
better hint to the compiler about what to do? (i.e. == faster).

Has anyone else considered this issue? Are there any better ways to
achieve the same effect? Is there a standard alternative to _msize
(that doesn't involve stamping the size into the memory myself)?

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

llewelly

unread,
Apr 29, 2004, 7:54:49 AM4/29/04
to
andy.p...@dagroupplc.com (Andy Paterson) writes:

> Hello people,
> I've gathered the following fact from reading various posts on this
> list:
> An overloaded delete operator cannot be explicitly called. You can
> overload delete, but it's only every called when an equivalently
> overloaded new throws an exception.

[snip]

This is only true for placement delete. User-defined Non-placement
delete can be called by an ordinary delete expression. See the
note in 5.3.5/8 .

Though it is seldom a problem in real code, it seems ugly to me that
standard requires that a programmer wishing to replace placement
delete write two functions with different names, but having
essentialy the same behavior.

Thomas Richter

unread,
Apr 29, 2004, 5:50:02 PM4/29/04
to
Hi,

> An overloaded delete operator cannot be explicitly called. You can
> overload delete, but it's only every called when an equivalently
> overloaded new throws an exception.

That's quite wrong. An overloaded delete operator will be called
whenever the class that overloads it gets deleted. You can call
it with exactly the same syntax the "global"/regular delete operator
gets called. Same goes with operator new, operator new[] and
operator delete[].

What you mean is most likely the calling syntax of the placement
versions of operator new, new[], delete and delete[]. The "new"
placement versions are directly callable, but the placement(!) delete's
are not. If you explicitly delete an object, its own - or the global -
operator delete is called, never the placement version. The placement
version is only called if you construct an object in the constructor
of another using placement new (or placement new[]) and the construction
of the containing object fails after the contained object is constructed.

> My question is, are there any clever workarounds for this?

> Hopefully, I am missing something important, but as it stands this is
> pretty annoying.

As long as you don't need to do something fancy with placement new,
(IMHO misnomen for "new with arguments") there's nothing to work around.

> Context: I've got multiple DLLs in my project, and I want to
> supplement the normal new and delete operators with DLL safe versions,
> looking a bit like this:
> (note: I'm paraphrasing my code here... expect errors)

/* snip */

I wouldn't approach your problem like this; the main problem is that
you place an operator new in the global namespace of the DLL, and from
there it will polute the global namespace. I don't know whether DLLs
will export this symbol to the environment using the DLL (I'm not a
Win expert, so the answer is "maybe not"), but it's IMHO not a good
thing to do. Instead, build an empty base object ("class
MyDLLBaseObject") and provide this object with your specialized
versions of operator new and operator delete, plus their array
conterparts. Derive all objects within your DLL from this base, and
you should be fine when allocating them.

So long,
Thomas

news

unread,
Apr 29, 2004, 6:06:27 PM4/29/04
to
"Andy Paterson" <andy.p...@dagroupplc.com> wrote in message
news:27f2f72a.04042...@posting.google.com...
Hi,

> I've gathered the following fact from reading various posts on this
> list:
> An overloaded delete operator cannot be explicitly called. You can
> overload delete, but it's only every called when an equivalently
> overloaded new throws an exception.
More correctly: when the equivalently overloaded new succeeds,
and the constructor of the allocated object(s) throws.

> My question is, are there any clever workarounds for this?

What do you mean? It is a feature ;)

There is an asymmetry here, but this is the way it is:
- you cannot explicitly call a constructor
- you cannot invoke a placement-version of the delete operator
I agree it is not nice -- but is it such a terrible thing?
(ok, it is a wart, like there are so many in C++...)

I do not think that using an explicitly named deletion function
is much worse than an overload of delete.
E.g.:
delete_DLLSafe (myPtr); // calling destructor explicitly
delete (DLLSafe) myPtr ; // any better? big difference?

> Also, surely having an explicitly called overloaded delete would be a
> better hint to the compiler about what to do? (i.e. == faster).


I do not think that any performance difference is to be expected.

> Has anyone else considered this issue? Are there any better ways to
> achieve the same effect? Is there a standard alternative to _msize
> (that doesn't involve stamping the size into the memory myself)?


A valuable improvement, however, would be to override the global
operator delete(void*) function, and implement it so that it
automatically detects, based on the address of the block to
be freed, which deletion function is to be called.
Is it an established performance problem to use DLL-safe
allocation/deallocation functions for all calls?

Workarounds? I don't know. But there are design alternatives,
including maybe:
- Have the DLL return pimpl-based statically allocated objects,
which internally manage reference counting, or some type of
smart pointer. (along the lines of what COM does...).
- Let the DLL provide class-level overrides of the new/delete
functions for the objects that it returns.


Regards,
Ivan
--
http://ivan.vecerina.com/contact/?subject=NG_POST <- e-mail contact form

Le Chaud Lapin

unread,
Apr 30, 2004, 10:50:30 PM4/30/04
to
andy.p...@dagroupplc.com (Andy Paterson) wrote in message news:<27f2f72a.04042...@posting.google.com>...
[snipped]

> My question is, are there any clever workarounds for this?
> Context: I've got multiple DLLs in my project,
[snipped]

I am guessing you are worried about heap-mismatch problem. If you are
not, then this post is irrelevant so please ignore :-)

The solution to the heap-mismatch problem is the same as the solution
to the delete-via-pointer-to-base problem: make the destructor
virtual.

In the latter problem, you do not want the compiler to think that the
pointer is pointing to an object whose class is 'Base' when you know
well that it is 'Derived'. Undefined behaviour might result. You
avoid this problem by making the destructor virtual, and then, (and
this a critical point which I think deserved far more exposition than
it received), the "free()" phase of the delete operation is effected
from within the destructor. Since the destructor is called virtually,
the code actually implementing it could be anywhere within the address
space of the process.

This implies that if the object is allocated on the DLL's heap, any
invocation of delete on a pointer to the object will result in the
call to the destructor that is inside the DLL, and that destructor
will have no ambiguity whatsoever about the size of the space it is
about to deallocate for the object. After all, when the object was
made, it was the DLL'S ::operator new who was the one who stashed the
appropriate pointer for the virtual function table according to the
type/size of the object. The converse is true for EXE's.

What this means is that you can freely pass pointers to dynamically
allocated objects between EXE's and DLL's, invoking delete on them at
will, as long as the objects' destructors are virtual. This also
explains why Windows COM hosts have no problem 'deleting' COM objects
that you manufacture inside your in-process DLL'S inspite of the
different heaps: the destructors of COM objects are always virtual.
The same reasoning applies to other OS's, of course.

In situations where the destructor is not virtual, and you are not
permitted to edit the class definition, may I suggest a
Virtually_Destructible<> template. Its raison d'etre is to force an
arbitrary class to have a virtual destructor:

template <typename Element> struct Virtually_Destructible : public
Element
{
Virtually_Destructible () {}
Virtually_Destructible (const Element &element) : Element(element)
{}
virtual ~Virtually_Destructible () {}
} ;

Let's say you have a class that absolutely positively cannot have a
virtual destructor under normal circumstances, but whose objects will
sometimes need to be passed between modules (heaps). I have one such
class: Digital_Address, whose objects are 64-bits wide, fit real
nice and snug inside certain 64-bit registers, and bloating them even
4 bytes is not an option. But I sometimes need to pass these objects
from DLL to EXE or vice-versa: I virtualize the destructor:

Virtually_Destructible<Digital_Address> *p =

new Virtually_Destructible<Digital_Address>();

Note that you can supply a singleton argument to the constructor if
you want (not illustrated here).

This allows the best of both worlds: you do not contaminate your
class with unecessary virtual functions, and you get inter-module
deletion without having to think about who new'ed what when or where.

-Chaud Lapin-

Andy Paterson

unread,
Apr 30, 2004, 11:02:07 PM4/30/04
to
Many thanks to all the replies.
I made some naming and usage errors in my first post;
Both llewelly and Thomas Richter point out that it's 'placement
delete' and not 'overloaded delete'.
I should have specified also that I was meaning to create placement
versions of the global new and delete, rather than class based ones.
My principle moan was that you couldn't create a global placement
delete. Apologies for not making this clear.
Thomas; I do worry about polluting the global namespace, but I figured
that by overloading new with my own types would not infringe anyone
elses global 'new' operator.
Ivan; yeah, it's wierd about no explicit constructor calls, and no
global placement delete. Maybe there is a good reason behind these
'warts'? I've gone down the route you suggest, with an explict
function call, as opposed to a global delete operator that can detect
how the memory was allocated (that, I think, would be pollution :),
but I do now stamp the first byte of memory with the type of
allocation it was (I support plain, DLL safe, tracked, and thread safe
tracked, in various combinations), so the Delete function call can
work out how to free the memory safely.
I've discarded the class new solution (possibly incorrectly) because I
wanted DLL safe, tracked allocations to be available for all types,
not just those derived from a specific class.

Thanks again,
Andy

Andy Paterson

unread,
May 1, 2004, 8:10:42 AM5/1/04
to
OK, I think I have a more compelling reason for a global placement
delete (well, a delete[] at least):

If you write a placement new[], then you -have- to stamp the size of
the object being created into the allocated memory (and there is no
particularly easy way to do this either). The reason for this being:

Because there is no placement delete[], you must write a function like
this:

template <typename T>
void _MyDelete(T* pMem)
{
if (pMem != 0)
{
T* const pLast = pMem + (BytesAllocatedToMem(pMem) / sizeof(T));
for (T* pCur = pMem; pCur != pLast; (pCur++)->~T());
FreeMyMemory(pMem);
}
}

(BytesAllocatedToMem() represents a call like _msize(), that gets the
number of bytes allocated at a given pointer)

What's obvious to me now (doh!) is that if you create a huge derived
object using your placement new[], assign the result to a small base
class pointer, then try and call _MyDelete() on that pointer, things
will go horribly wrong (because it will be trying to call the
destructor for a count of (BytesAllocated / sizeof(BaseClass)) base
class objects, instead of a count of (BytesAllocated /
sizeof(DerivedClass)) derived class objects. Also, the pointer
arithmetic will break things too):

// Seem reasonable enough...
CBaseClass* pBase = new (MyPlacementNew) CHugeDerivedClass[100];

// ...Will break spectacularly
_MyDelete(pBase);

I have not yet found an API call to extract the size of objects
allocated at a given pointer; there may be one, which would be great,
but I doubt it would be standard (I feel wierd enough about using
_msize() in such a fundamental operation).
Therefore, Stamping must be done (only for new[], though). This is
frustrating, because I am sure that for delete[] to work, the object
size value must be floating around somewhere anyway. It's extra
overhead, and extra nasty code.
Why jump through all these hoops, when a nice placement delete[] would
do the work for you? And if there's a placement delete[], it would be
an affront to nature not to have a placement delete also.

Please explain where I have gone wrong, somebody... :)

Andy Paterson

unread,
May 3, 2004, 5:42:35 AM5/3/04
to
> In the latter problem, you do not want the compiler to think that the
> pointer is pointing to an object whose class is 'Base' when you know
> well that it is 'Derived'. Undefined behaviour might result.

Totally agreed. I think that I put virtual destructors in my examples.
If I did not, then it definitely needs pointing out.
However, this:


>You
> avoid this problem by making the destructor virtual, and then, (and
> this a critical point which I think deserved far more exposition than
> it received), the "free()" phase of the delete operation is effected
> from within the destructor. Since the destructor is called virtually,
> the code actually implementing it could be anywhere within the address
> space of the process.
> This implies that if the object is allocated on the DLL's heap, any
> invocation of delete on a pointer to the object will result in the
> call to the destructor that is inside the DLL, and that destructor
> will have no ambiguity whatsoever about the size of the space it is
> about to deallocate for the object.

... I do not think is true (maybe I got the wrong end of the stick,
though). Destructors have nothing to do with memory allocation. An
object's destructor can be called for objects that are heap allocated,
stack allocated, globals, functions statics, whatever.
Just making a destructor virtual does not ensure that it's allocated
memory is de-allocated in the module in which the destructor is
implemented (unless you have a wierd compiler :)

Also, check out the following code:
<a.dll code>
class A
{
virtual ~A() { }
};
<b.dll code>
A* pObject = new A;
delete pObject;

If what you say is true, the initial 'new' would allocate memory from
DLL b's heap, then the delete would try to free it from from 'a''s
heap, which would cause an error.
Normally, 'delete' is an inline wrapper for 'free', that has the
automagic property of calling the object's destructor prior to the
call.

If you do not include a destructor in a class, and attempt to call it
(implicitly or explicitly) in another DLL, then the destructor will be
automatically generated by the compiler for that module, leading to
possible memory deallocation issues.

Therefore, it is very important to (i) Make destructors virtual, and
(ii) always implement and export destructors (and copy constructors
and assignment operators too) in classes that you want to be DLL safe.

(phew... long message... sorry... last bit)

Given all of these things, I would avoid something like your
Virtually_Destructible class. The point of a virtual destructor is to
allow deletion of base class pointers; on deletion, code will be
generated to look up the virtual table (or however the virtual calling
mechanism is implemented) of the doomed object to find and call it's
derived class destructor.
Therefore, the point of your Virtually_Destructible class is kind of
lost, seeing as it is designed to be the topmost class in a hierarchy.
It provides neither DLL safety nor correct use of the virtual
destructor.

I cannot understand why your class works (maybe I have screwed up in
my reasoning :)
It may be that you are linking with one of the DLL safe C runtime
libraries, that provide cross DLL allocation/deallocation, or maybe
you Virtually_Destructible class is providing some sort of safety, but
I do not think it is in the way that you imagine it to.

Cheers,
Andy.

Le Chaud Lapin

unread,
May 5, 2004, 6:37:59 AM5/5/04
to
andy.p...@dagroupplc.com (Andy Paterson) wrote in message news:<27f2f72a.04050...@posting.google.com>...

> ... I do not think is true (maybe I got the wrong end of the stick,
> though). Destructors have nothing to do with memory allocation.

Actually, destructors and memory allocation were designed to work
together for situations like this.

> An object's destructor can be called for objects that are heap allocated,
> stack allocated, globals, functions statics, whatever.
> Just making a destructor virtual does not ensure that it's allocated
> memory is de-allocated in the module in which the destructor is
> implemented (unless you have a wierd compiler :)

I must admit, Visual Studio 6.0 has been known to eat my Otis
Spunkmeyer pastries while I am not looking. It also invokes virtual
destructors via a pointer to the vtable of an object.

> Also, check out the following code:
> <a.dll code>
> class A
> {
> virtual ~A() { }
> };
> <b.dll code>
> A* pObject = new A;
> delete pObject;
>
> If what you say is true, the initial 'new' would allocate memory from
> DLL b's heap, then the delete would try to free it from from 'a''s
> heap, which would cause an error.

No. The new will invoke malloc for (roughly) sizeof(A). Call the
constructor if any. Then *it will squirrel-away a pointer to the
vtable for A in the object.* There is nothing to keep the compiler
from generating destructors inside both A.DLL and B.DLL. After all,
the code is right there in its face, inline.

Remeber, the "free" phase of the delete operation is effectively
carried out by the destructor. If you consider that that the code
that deletes the object via a a pointer-to-base cannot, in general,
know a priori the size of the object being pointed to, then you will
see that the "free" part must neccesarily be carried out by the
virtual destructor, meaning, the code that is yielded once the vtable
dereferencing has occurred.

> Normally, 'delete' is an inline wrapper for 'free', that has the
> automagic property of calling the object's destructor prior to the
> call.

That's true, but not for classes with virtual destructors. That's why
you should wrap the ones that are devoid of virtual destructors with
Virtually_Destructible<>

> If you do not include a destructor in a class, and attempt to call it
> (implicitly or explicitly) in another DLL, then the destructor will be
> automatically generated by the compiler for that module, leading to
> possible memory deallocation issues.

True again, but as I said, make the class have a destructor, a virutal
one in particular.

> Therefore, it is very important to (i) Make destructors virtual, and
> (ii) always implement and export destructors (and copy constructors
> and assignment operators too) in classes that you want to be DLL safe.

In general, it is not possible to have generic (compiler-supplied)
copy constructors or assignment from DLL to EXE or vice-versa for
classes that have internal memory management. The problem is that if
you attempt to assign from DLL to EXE (or vice-versa), the assignment
operator code in the DLL will attempt to destroy the memory of the EXE
object, causing a heap-mismatch exception, which you have certainly
discovered already. The same rule applies to cross-module
initialization, although more subtle, since often zero is used to mean
not-yet-initialized, and zero is zero whether inside EXE or DLL. If
you want to maintain a basic level of elgance, the method I
recommended is quasi-optimal.

> Given all of these things, I would avoid something like your
> Virtually_Destructible class. The point of a virtual destructor is to
> allow deletion of base class pointers; on deletion, code will be
> generated to look up the virtual table (or however the virtual calling
> mechanism is implemented) of the doomed object to find and call it's
> derived class destructor.

Right, so let's say you made a function in B.DLL that returned a
pointer to an object that B made:

BThing *pThing = new BThing; // Now give pThing to A

Then in A, you wrote:

delete pThing;

The compiler will:

1. Know that the destructor of BThing is virtual.
2. Use pThing to find the beginning of the object.
3. Retrieve the vtable pointer from the beginning of the object.
4. Locate the pointer to the destructor in vtable and invoke it.
5. Let the destructor be the one to effectively call free.

Certainly you are already convinced of #5 - it is the classical reason
for making destructors virtual, whether DLL's are involved or not.

Therefore, it is also reasonable that, if B is able to return a
pointer to a constructed object, then all of that object's code,
including a virtual destructor, exists wholly within B. Note that
this does not preclude virtual destructor code from existing in A, but
the fact that the DLL linked with no errors means that the linker is
happy that all the requisite code that is needed by B is within B.

> Therefore, the point of your Virtually_Destructible class is kind of
> lost, seeing as it is designed to be the topmost class in a hierarchy.
> It provides neither DLL safety nor correct use of the virtual
> destructor.

???

> I cannot understand why your class works (maybe I have screwed up in
> my reasoning :)

You tell me. :-)

> It may be that you are linking with one of the DLL safe C runtime
> libraries, that provide cross DLL allocation/deallocation, or maybe
> you Virtually_Destructible class is providing some sort of safety, but
> I do not think it is in the way that you imagine it to.

Nah, none of that. As a matter of fact, 95% of my classes have no
virtual functions at all. The ones that do just happen to be those
that need to cross module boundaries, where "new" them in a DLL and
"delete" them in EXE using without any problems. If I hever have a
"rigid" object that I need to pass from DLL, I use
Virtually_Destructible<>.

If you are not convinced of any of this, all you have to do is "new"
an object in DLL, return pointer to it to EXE, then invoke delete on
the pointer in EXE, and single-step through delete operation (in
assembly mode if destructor is trivial). Keep an eye out for where
the cursor goes. :-)

-Chaud Lapin-

Andy Paterson

unread,
May 5, 2004, 3:34:41 PM5/5/04
to
unorigina...@yahoo.com (Le Chaud Lapin) wrote in
news:fc2e0ade.04050...@posting.google.com:

> andy.p...@dagroupplc.com (Andy Paterson) wrote in message
> news:<27f2f72a.04050...@posting.google.com>...
> > ... I do not think is true (maybe I got the wrong end of the stick,
> > though). Destructors have nothing to do with memory allocation.
>
> Actually, destructors and memory allocation were designed to work
> together for situations like this.
>
> > An object's destructor can be called for objects that are heap
> > allocated, stack allocated, globals, functions statics, whatever.
> > Just making a destructor virtual does not ensure that it's allocated
> > memory is de-allocated in the module in which the destructor is
> > implemented (unless you have a wierd compiler :)
>
> I must admit, Visual Studio 6.0 has been known to eat my Otis
> Spunkmeyer pastries while I am not looking. It also invokes virtual
> destructors via a pointer to the vtable of an object.
>

Visual Studio ate my hamster.

Your reply is making me doubt lots of things that I thought I knew for sure
about memory allocation in DLLs, so I decided to test my assumptions. They
do indeed bear out your assertions about Virtually_Destructible.

I've had a quick look at the disassembly of the tests, and I think that
this is what is going on:

If you try to delete an object without a virtual destructor, some code is
automatically created ('scalar/vector deleting destructor') inside the
calling DLL. This wraps up a call to the object's destructor, and the call
to delete, so it would look like:

void ScalarDeletingDestructor(CMyObject* pObj)
{
pObj->~CMyObject();
free(pObj);
}

Calling this on a CMyObject allocated in another DLL would indeed cause a
heap mismatch error.

If you do include a virtual destructor, then a scalar deleting destructor
is automatically defined and added to the class vtable. This essentially
makes the deallocation occur within the source DLL, instead of the caller
DLL.

Is this about right?

Incidentally, this may be why placement delete is not possible - because by
calling 'delete', you aren't really calling it; you are in fact calling an
automatically generated class method which takes no arguments. To generate
ScalarDeletingDestructor with arguments would require a-priori knowledge of
the parameters to all your overloaded global delete operators.

I still don't quite understand how the reverse works (creating a new object
in one DLL and freeing it in the source DLL) - I will examine further.

Finally, having said all this, I am still dubious about the
Virtually_Destructible class:

In "a.dll"

class CBase
{
public :
~CBase() { std::cout << "In CBase destructor\n"; }
};

class CDerived
{
public :
~CDerived() { std::cout << "In CDerived destructor\n"; }
};

__declspec(dllexport) Virtually_Destructible<CBase>* _CreateBase()
{
return new CBase;
}

__declspec(dllexport) Virtually_Destructible<CDerived>* _CreateDerived()
{
return new CDerived;
}

In "b.dll"


// Problem 1
CBase* pObj1 = _CreateDerived();

// Problems 2 & 3
delete pObj1;

// Problem 4
Virtually_Destructible<CBase> pObj1 = _CreateDerived();

Problem 1: This is a valid and desirable assignment. It leads to the
following issues:

Problem 2: Because the destructor in CBase is not virtual, 'delete pObj'
will only cause "In CBase destructor" to be output.

Problem 3: Because the heap-mismatch safety is provided by the
Virtually_Destructible virtual destructor, this delete statement will cause
a heap-mismatch error (because you are deleting a CBase*, not a
Virtually_Destructible*)

Problem 4: Illegal assignment - Virtually_Destructible<CBase> is not a base
class of Virtually_Destructible<CDerived>. It would be nice, though.


Cheers,
Andy

Le Chaud Lapin

unread,
May 6, 2004, 9:37:44 AM5/6/04
to
Andy Paterson <andy.p...@dagroupplc.com> wrote in message news:<Xns94E09BEF683C3an...@62.253.162.204>...

Moderator & Andy:

Please forgive the long reply. I thought it would be instructive to
expound upon this topic a bit since it is something that many of us
think about but only look at when we are debugging.

> If you try to delete an object without a virtual destructor, some code is
> automatically created ('scalar/vector deleting destructor') inside the
> calling DLL. This wraps up a call to the object's destructor, and the call
> to delete, so it would look like:
>
> void ScalarDeletingDestructor(CMyObject* pObj)
> {
> pObj->~CMyObject();
> free(pObj);
> }

This is true. The compiler knows that there will be many places where
it will need to do a destruct/free sequence, either when it can see
that that is what is necessary, or as figuring out that the it is what
necessary after ascertaining the exact nature of the object being
pointed to, so it packages this sequence in a function. Contrary to
popular belief, it does not call destruct/free willy nilly in-line.

> If you do include a virtual destructor, then a scalar deleting destructor
> is automatically defined and added to the class vtable. This essentially
> makes the deallocation occur within the source DLL, instead of the caller
> DLL.
> Is this about right?

Yes.

Incidentally, this may be why placement delete is not possible -
because by
> calling 'delete', you aren't really calling it; you are in fact calling an
> automatically generated class method which takes no arguments. To generate
> ScalarDeletingDestructor with arguments would require a-priori knowledge of
> the parameters to all your overloaded global delete operators.

Right. But you certainly do not want to encourage mismatches for
new/delete. You can still achieve this type of abuse through
class-specific delete. Note again that class-specific delete acts in
"smart" mode.



> I still don't quite understand how the reverse works (creating a new object
> in one DLL and freeing it in the source DLL) - I will examine further.

We shall examine this in depth below.



> Finally, having said all this, I am still dubious about the
> Virtually_Destructible class:
>
> In "a.dll"
>
> class CBase
> {
> public :
> ~CBase() { std::cout << "In CBase destructor\n"; }
> };
>
> class CDerived
> {
> public :
> ~CDerived() { std::cout << "In CDerived destructor\n"; }
> };
>
> __declspec(dllexport) Virtually_Destructible<CBase>* _CreateBase()
> {
> return new CBase;
> }
>
> __declspec(dllexport) Virtually_Destructible<CDerived>* _CreateDerived()
> {
> return new CDerived;
> }
>
> In "b.dll"
>
>
> // Problem 1
> CBase* pObj1 = _CreateDerived();
>
> // Problems 2 & 3
> delete pObj1;
>
> // Problem 4
> Virtually_Destructible<CBase> pObj1 = _CreateDerived();
>
> Problem 1: This is a valid and desirable assignment. It leads to the
> following issues:
>
> Problem 2: Because the destructor in CBase is not virtual, 'delete pObj'
> will only cause "In CBase destructor" to be output.

That's right.



> Problem 3: Because the heap-mismatch safety is provided by the
> Virtually_Destructible virtual destructor, this delete statement will cause
> a heap-mismatch error (because you are deleting a CBase*, not a
> Virtually_Destructible*)

Right again, so you need to make sure that the pointer you are
returning is a pointer to a Virtually_Destructible:

__declspec(dllexport) Virtually_Destructible<CBase>* _CreateBase()
{

return new Virtually_Desctructible<CBase>();


}

> Problem 4: Illegal assignment - Virtually_Destructible<CBase> is not a base
> class of Virtually_Destructible<CDerived>. It would be nice, though.
>

Hmm..I could be wrong, but I think that compiler would barf on that.

Now let's clear up the fuzziness around
constructor/destructor/new/delete.

Consider the following program of four classes. We have

1. regular Base
2. regular Derived
3. virtual Base
4. and Virtual Derived

The virtual Derived is virtual because it has a virtual function, not
because it's Base does. Let us examine malloc/construct/destruct/free
sequence for objects of each of these four classes below. The "cout'"
are only here to add meat to the code:

#include <iostream.h>
struct Base
{
Base (){cout << "Base::Base()" << endl;}
~Base () {cout << "Base::~Base()" << endl;}
} ;

struct VBase
{
VBase() {cout << "Base::Base()" << endl;}
virtual ~VBase() {cout << "Base::~Base()" << endl;}
} ;

struct Derived : Base
{
Derived () {cout << "Dervied::Derived()" << endl;}
~Derived () {cout << "Derived::~Derived()" << endl;}
} ;

struct VDerived : Base
{
VDerived () { cout << "Dervied::VDerived()" << endl;}
virtual ~VDerived () {cout << "VDerived::~Derived()" << endl;}
} ;

int main ()
{
Base *pBase = new Base; delete pBase;
Derived *pDerived = new Derived; delete pDerived;
VBase *pVBase = new VBase; delete pVBase;
VDerived *pVDerived = new VDerived; delete pVDerived;

return 0;
}

On the matter of construction: For each of the objects, operator new
is called to make memory for the object, then the constructor is
called on the space. If the object is an aggregate, its constructor
calls the bases' constructors (recursively of course), and the member
objects' constructors on the space also. That was easy. Now let's look
at destruction:

For Base, a 'scalar deleting destructor' function is made. This
function represents a destructor/free sequence *specifically* made for
Base. This function is *not* the function that begins with the tilde
(~) as you discovered. The (~) function is called by Base::'scalar
deleting destructor'. The 'scalar deleting destructor also contains a
call to delete. Let's look at pDerived's 'scalar deleting
destructor':

66: delete pDerived;
004010D6 mov eax,dword ptr [pDerived]
004010D9 mov dword ptr [ebp-3Ch],eax
004010DC mov ecx,dword ptr [ebp-3Ch]
004010DF mov dword ptr [ebp-38h],ecx
004010E2 cmp dword ptr [ebp-38h],0
004010E6 je main+0F7h (004010f7)
004010E8 push 1
004010EA mov ecx,dword ptr [ebp-38h]
//// RIGHT HERE. SEE? NO TILDE (as you discovered)
004010ED call Derived::`scalar deleting destructor'
(00401570)
004010F2 mov dword ptr [ebp-6Ch],eax
004010F5 jmp main+0FEh (004010fe)
004010F7 mov dword ptr [ebp-6Ch],0

Inside 'scalar deleting destructor' is where the ~ function is called,
along with "free". Naturally, if the object were located on the stack
based, the ~ function would have been called directly, and no 'scalar
deleting destructor' function would have been necessary:

Derived::`scalar deleting destructor':
00401570 push ebp
00401571 mov ebp,esp
00401573 push ecx
00401574 mov dword ptr [ebp-4],0CCCCCCCCh
0040157B mov dword ptr [ebp-4],ecx
0040157E mov ecx,dword ptr [this]
// HERE IS THE CALL Derived::~Derived()
00401581 call Derived::~Derived (004015b0)
00401586 mov eax,dword ptr [__flags]
00401589 and eax,1
0040158C test eax,eax
0040158E je Derived::`scalar deleting destructor'+2Ch
(0040159c)
00401590 mov ecx,dword ptr [this]
00401593 push ecx
// AND HERE IS THE free()
00401594 call operator delete (00403c30)
00401599 add esp,4
0040159C mov eax,dword ptr [this]
0040159F add esp,4
004015A2 cmp ebp,esp
004015A4 call __chkesp (004041d0)
004015A9 mov esp,ebp
004015AB pop ebp
004015AC ret 4
004015AF int 3

Note that Derived::~Derived() knows to call Base::~Base(). ~Base is
*not* called by Derived's scalar deleting destructor. This should be
intutitively obvious. If the heap were not involved, there would be
no need for 'scalar deleting destructor's, and so no place to put the
Base::~Base().

For both VBase and VDerived, there is still a 'scalar deleting
destructor', but it is found by examining the vtable for the object.
Note how now, not only is there no mention of the ~ functions, but
there is neither no mention of 'scalar deleting destructor'. All this
must be determined at run-time solely by traversing the pointer to the
object:

68: delete pVBase;
0040113F mov ecx,dword ptr [pVBase]
00401142 mov dword ptr [ebp-4Ch],ecx
00401145 mov edx,dword ptr [ebp-4Ch]
00401148 mov dword ptr [ebp-48h],edx
0040114B cmp dword ptr [ebp-48h],0
0040114F je main+16Bh (0040116b)
00401151 mov esi,esp
00401153 push 1

// Move pointer to object into EAX:

00401155 mov eax,dword ptr [ebp-48h]

// Move first (4-byte) value at beginning of object into EAX
// This 4-byte value is pointer to virtual function table

00401158 mov edx,dword ptr [eax]

// Put "this" pointer in ECX preparing for call to
// 'scalar deleting destructor'

0040115A mov ecx,dword ptr [ebp-48h]

// Call 'scalar deleting destructor' *NOT* Base::~Base()
// SDD is at the address specified by initial 4-bytes in vtable
0040115D call dword ptr [edx] <<< call SDD
0040115F cmp esi,esp
00401161 call __chkesp (004041d0)
00401166 mov dword ptr [ebp-74h],eax
00401169 jmp main+172h (00401172)
0040116B mov dword ptr [ebp-74h],0

You can see that, at no time does is there an attempted to ascertain
the type of the object (using RTTI). The processor simply invokes
whatever pointer is located in the right spot of the vtable of the
object for destruction. Once this happens, a 'scalar deleting
destructor' is found, and the process is exactly the same as before,
most notably that the 'scalar deleting destructor' does *not* call the
(~) functions of any subobjects, but lets the master object's (~)
function do that.

So in the cass of Virtually_Destructible being inside a DLL, whenever
you invoke delete on a pointer to it, the compiler knows that the
object has a virtual destructor and discovers the 'scalar deleting
destructor' for Virtually_Destructible by accessing the object's
vtable. That function then calls
Virtually_Destructible<>::~Virtually_Desctructible<>() on the space
that the pointer points to, which in turn, calls Base::Base~() on that
same space, but at the right spot (in this case, the beginning of
thespace). It also calls "free()" on "this" pointer using
sizeof(Virtuall_Destructible<>). Naturally, 'scalar deleting
destructor' is made spefically for Virtually_Destructible, so it has
no doublt whatsoever about what the pointer is pointing to, and that
it is slightly larger than the thing being wrapped. Since 'scalar
deleting destructor'is in the DLL and not the EXE (otherwise linking
of DLL would not work), there is no heap mismatch problem.

Hope this makes things a bit clearer.

-Chaud Lapin-

Dave Moore

unread,
May 6, 2004, 12:21:44 PM5/6/04
to
Andy Paterson <andy.p...@dagroupplc.com> wrote in message news:<Xns94E09BEF683C3an...@62.253.162.204>...

> Finally, having said all this, I am still dubious about the
> Virtually_Destructible class:
>
> In "a.dll"
>
> class CBase
> {
> public :
> ~CBase() { std::cout << "In CBase destructor\n"; }
> };
>
> class CDerived
> {
> public :
> ~CDerived() { std::cout << "In CDerived destructor\n"; }
> };

I guess you meant to publicly derive CDerived from CBase?

> __declspec(dllexport) Virtually_Destructible<CBase>* _CreateBase()
> {
> return new CBase;
> }
>
> __declspec(dllexport) Virtually_Destructible<CDerived>* _CreateDerived()
> {
> return new CDerived;
> }
>
> In "b.dll"
>
>
> // Problem 1
> CBase* pObj1 = _CreateDerived();
>
> // Problems 2 & 3
> delete pObj1;
>
> // Problem 4
> Virtually_Destructible<CBase> pObj1 = _CreateDerived();
>

[problem descriptions snipped]

It seems like everything you tried to do above and all of the problems
you described stem from a fundamental misunderstanding about what the
Virtually_Desctructible template is intended for. It is *only* for
providing virtual dtors to un-editable classes that *must* for some
reason (e.g. DLL/EXE heap compatability) have virtual dtors. It
cannot magically fix inheritance patterns that are already screwed up,
like the one you gave above, where CBase lacks a virtual dtor, but has
(presumably) been used as a public base class for CDerived.

What you could do however, is derive CDerived from
Virtually_Destructible<CBase>:

class CDerived : public Virtually_Destructible<CBase> {
// your def here
};

Then, you can just say

typedef Virtually_Destructible<CBase> safeCBase;
safeCbase* p = new CDerived; // in first DLL

delete p; // in another dll or exe file

and everything will happen safely and naturally (between consenting
DLL's and EXE's). At least that is how I currently understand the
situation .. I could be confused 8*).

FWIW I think the Virtually_Destructible (V_D) template is a nice clean
solution because it "stays out of the way" in the sense that all of
the public members of the templated base class are accessible *without
forwarding* through the V_D base object. Of course you have to
remember to declare pointers to V_D objects and not the raw base
objects, but that can be made easier by the use of typedefs, as I did
above.

The V_D template also seems like a good solution to the problem of
publically deriving from STL container classes, which can be handy for
class design (i.e. my_new_class has all/some of the functionality of
an STL container, with a few useful restrictions and/or bells and
whistles). This has been previously identified in this NG and related
ones as bad (or at least potentially dangerous) technique, since the
STL containers lack virtual dtors.

I would appreciate comments from Mr. Lapin (and others) about the pros
& cons of the above idea.

TIA, Dave Moore

Jake Montgomery

unread,
May 7, 2004, 8:13:44 AM5/7/04
to

Le Chaud Lapin wrote:
[snip]

>
>
> The virtual Derived is virtual because it has a virtual function, not
> because it's Base does. Let us examine malloc/construct/destruct/free
> sequence for objects of each of these four classes below. The "cout'"
> are only here to add meat to the code:
>
> #include <iostream.h>
> struct Base
> {
> Base (){cout << "Base::Base()" << endl;}
> ~Base () {cout << "Base::~Base()" << endl;}
> } ;
>
> struct VBase
> {
> VBase() {cout << "Base::Base()" << endl;}
> virtual ~VBase() {cout << "Base::~Base()" << endl;}
> } ;
>
> struct Derived : Base
> {
> Derived () {cout << "Dervied::Derived()" << endl;}
> ~Derived () {cout << "Derived::~Derived()" << endl;}
> } ;
>
> struct VDerived : Base
> {
> VDerived () { cout << "Dervied::VDerived()" << endl;}
> virtual ~VDerived () {cout << "VDerived::~Derived()" << endl;}
> } ;
>
> int main ()
> {
> Base *pBase = new Base; delete pBase;
> Derived *pDerived = new Derived; delete pDerived;
> VBase *pVBase = new VBase; delete pVBase;
> VDerived *pVDerived = new VDerived; delete pVDerived;
>
> return 0;
> }
>
>

[Snip]

>
> So in the cass of Virtually_Destructible being inside a DLL, whenever
> you invoke delete on a pointer to it, the compiler knows that the
> object has a virtual destructor and discovers the 'scalar deleting
> destructor' for Virtually_Destructible by accessing the object's
> vtable. That function then calls
> Virtually_Destructible<>::~Virtually_Desctructible<>() on the space
> that the pointer points to, which in turn, calls Base::Base~() on that
> same space, but at the right spot (in this case, the beginning of
> thespace). It also calls "free()" on "this" pointer using
> sizeof(Virtuall_Destructible<>). Naturally, 'scalar deleting
> destructor' is made spefically for Virtually_Destructible, so it has
> no doublt whatsoever about what the pointer is pointing to, and that
> it is slightly larger than the thing being wrapped. Since 'scalar
> deleting destructor'is in the DLL and not the EXE (otherwise linking
> of DLL would not work), there is no heap mismatch problem.
>
> Hope this makes things a bit clearer.
>

Indeed it does ... thanx.
I was wondering (since you seem so knowledgeable) how delete[] is
handled in the similar case:

VDerived *pMany = new VDerived[100];
delete[] pMany;

Thanx, Jake

Andy Paterson

unread,
May 8, 2004, 5:52:58 AM5/8/04
to
Hi Mr. Lapin,
Many thanks for your rigorous treatment of this problem.
I don't think
you left anything out...

I think the one fundamental thing I was missing when I
was checking out
the disassembly of virtual destructors was that, because
Virtually_Destructible is a template class, when you
create a
Virtually_Destructible object, the template is instanced
at that point,
which creates the scalar deleting destructor in the
vtable, in the DLL
where the call is made.

This, by definition, means that wherever you call the
delete from, the
actual free is done by the scalar deleting destructor in
the DLL that
created the Virtually_Destructible object. Which is
counter-intuitive
template behaviour, but intuitive virtual function
method. Which
confused me.

I said:
>> I still don't quite understand how the reverse works
(creating a new
>> object in one DLL and freeing it in the source DLL) -
I will examine
>> further.

The point being that there is no reverse - the object is
ALWAYS freed in
the DLL it was created in.

Ummm.... if you see what I mean :)

My initial skepticism has passed - I will be using
Virtually_Destructible as a wrapper in future for
passing sealed objects
like STL classes and 3rd party code around (but I'll
still derive any
stuff that I want to be DLL safe from a base class with
a virtual
destructor, I think).

Having said all that, I am very happy with my new/
_SafeDelete
combination (I can use it for any type, not just those
that I can
decorate with Virtually_Destructible). The code also
does memory leak
detection, and can be optionally thread safe, all
through very simple
interface. If anyone is interested, I will post it up.

Andy

Andy Paterson

unread,
May 8, 2004, 6:02:02 AM5/8/04
to
My last posting of the message seemed to fail. Apologies if this gets
posted twice....


Whoops, yes, you were right. CDerived should have derived from CBase.
Writing too fast, I guess.

I do appreciate the use of Virtually_Destructible in terms of DLL safety
(now at least... took a bit of prodding from Mr. Lapin to get there :)

However, I think an important issue is that the Virtually_Destructible
object must publicly derive from it's DLL unsafe base class, which could
lead to a perfectly valid cast to the base class as I described (it
doesn't need to be part of a dodgy hierarchy like the one I posted).

Example:
In "a.dll"

typedef std::vector<int> TVector;

__declspec(dllexport) Virtually_Destructible<TVector>* _NewVector()
{
return new Virtually_Destructible<TVector>;
}

In "b.dll"

TVector* pVector = _NewVector(); // Valid cast, easy mistake to make
delete pVector; // Causes heap mismatch allocation error


Given this, what about a pointer wrapper class for managing
Virtually_Destructible objects, to ensure correct usage?


// Was Virtually_Destructible,renamed CVD for brevity in this example
// Note protected inheritance - can no longer mistakenly cast to T
template <typename T>
class CVD : protected T
{
public :

virtual ~CVD() { }

T* GetBase() { return this; }
const T* GetBase() const { return this; }

};


template <typename T>
class CVDPtr
{

CVD<T>* m_pObj;

public :

CVDPtr()
{
m_pObj = 0;
}

CVDPtr(CVD<T>* pObj)
{
m_pObj = pObj;
}

CVDPtr(const CVDPtr& Src) : m_pObj(Src.m_pObj)
{
}

operator CVD<T>* ()
{
return m_pObj;
}

operator const CVD<T>* () const
{
return m_pObj;
}

T* operator -> ()
{
return m_pObj ? m_pObj->GetBase() : 0;
}

const T* operator -> () const
{
return m_pObj ? m_pObj->GetBase() : 0;
}

CVDPtr& operator = (const CVDPtr& Src)
{
m_pObj = Src.m_pObj;
return *this;
}

// etc. (may require more operators to make it fully transparent)
};


// Example:

// In "a.dll"

class CMyClass
{
public :
void DoStuff();
};

__declspec(dllexport) CVDPtr<CMyClass> _NewMyClass()
{
return new CVD<CMyClass>;
}

// In "b.dll"

//CMyClass* pObj = _NewMyClass(); // ILLEGAL
CVDPtr<CMyClass> pObj = _NewMyClass(); // OK

pObj->DoStuff(); // CTest accessed via -> operator

delete pObj; // operator CVD<CMyClass>*() invoked by delete


Apologies in advance for any errors in the code; I hacked this together
quickly, and posted when it (seemed to) work.
What does everyone think?

Andy

Andy Paterson

unread,
May 8, 2004, 6:02:24 AM5/8/04
to

> Indeed it does ... thanx.
> I was wondering (since you seem so knowledgeable) how delete[] is
> handled in the similar case:
>
> VDerived *pMany = new VDerived[100];
> delete[] pMany;

I believe that, in this case, a 'vector deleting destructor' is created and
added to the vtable, instead of a 'scalar deleting destructor'.

Andy

Andy Paterson

unread,
May 9, 2004, 8:17:02 AM5/9/04
to

Example:
In "a.dll"

In "b.dll"

virtual ~CVD() { }

};

CVD<T>* m_pObj;

public :

CVDPtr()
{
m_pObj = 0;
}

CVDPtr(const CVDPtr& Src) : m_pObj(Src.m_pObj)
{
}

// etc... (requires more operators to make it fully transparent)
};


// Example:

// In "a.dll"

class CMyClass
{
public :
void DoStuff();
};

__declspec(dllexport) CVDPtr<CMyClass> _NewMyClass()
{
return new CVD<CMyClass>;
}

// In "b.dll"

//CMyClass* pObj = _NewMyClass(); // ILLEGAL
CVDPtr<CMyClass> pObj = _NewMyClass(); // OK

pObj->DoStuff(); // CTest accessed via -> operator

delete pObj; // operator CVD<CMyClass>*() invoked by delete


Apologies in advance for any errors in the code; I hacked this together
quickly, and posted when it (seemed to) work.
What does everyone think?

Andy

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Le Chaud Lapin

unread,
May 11, 2004, 6:13:35 AM5/11/04
to
Andy Paterson <andy.p...@dagroupplc.com> wrote in message news:<Xns94E28CAF63C2Dan...@62.253.162.203>...
[code sample snipped - see parent post]

> Apologies in advance for any errors in the code; I hacked this together
> quickly, and posted when it (seemed to) work.
> What does everyone think?

Your solution looks like it will work, but as always the case in
engineering solution, one has to ask, "When is enough, enough?"

What I mean is that your original solution would have require
conscientious application of a special operation (a memory allocating
function), and Virtually_Destructible<> eliminated the need for that,
but it had a flaw, which you fixed, so now there is not need for worry
about the problem you fixed, but with the side effect that you no
longer have the . operator to work with in your code:

Virtual_Destructible<Foo> x;

x.whatever()...// cannot do this anymore

I am a huge fan of the .operator. It aids in keeping the mind in the
land of form and structure.

If your goal is to keep people from making the mistake of assigning
the pointer returned by the DLL to a pointer to base, then in that
case, I would simply return a void pointer:

In DLL:

void * make_virturally_destructible_digital_address ()
{
return new Virtually_Destructible<Digital_Address>;
}

In EXE:

Digital_Address *p = make_virturally_destructible_digital_address ();

Compiler complains, forcing you to use reinterpret_cast, so that you
are reminded not to make the mistake you mentioned.

Note that do not propose this. As I said, I would never have trouble
remembering what type is what (and coding accordingly). My point
here, I guess, is that prudence dictates exercising restraint in
innovation.

But gain, it all depends on what the programmer feels comfortable
with.

-Chaud Lapin-

0 new messages