delete an object through its parent - virtual desctructors

M

Mosfet

Hi,

I ma developping a small framework exporting C functions but behind the
scene I am manipulating C++ objects. Memory management relies on
reference count and the base C "object" is called GDObject and is
actually a structure with a counter.


///////////////////////////////////////////////////////
// Public declaration for GDObject used to handle "object" life
__BEGIN_C_DECLS

typedef struct _GDObject GDObject;

typedef struct _GDObject
{
long cRefs;
} GDObject;

#define GDObject_AddRef(x) _GDObject_AddRef((GDObject*) (x));
#define GDObject_Release(x) _GDObject_Release((GDObject*) (x));

GYNOID_API long
_GDObject_AddRef(GDObject* gdObject);

GYNOID_API long
_GDObject_Release(GDObject* gdObject);

__END_C_DECLS

///////////////////////////////////////////////////////
//Private implementation of GDObject :
....
long
_GDObject_Release(GDObject* gdObject)
{
LONG lRef;

if (!gdObject )
return 0;

if (gdObject->cRefs == 1)
{
lRef = 0;
gdObject->cRefs = 0;
}
else
{
lRef = InterlockedDecrement(&(gdObject->cRefs));
}

if (lRef == 0)
{
delete gdObject;
}

return (ULONG)lRef;
}




So now let's consider the address book api defined like this :

// Public declaration
typedef GDObject GDAddrBook;


GYNOID_API ErrorCode
GDAddrBook_Alloc(GDAddrBook** gppAddrbook);

GYNOID_API ErrorCode
GDAddrBook_Init(GDAddrBook* gpAddrbook, OsHandle osParam);

// Private implementation
struct os_addrbook : public GDObject
{
os_addrbook()
{
cRefs = 1;
}
~os_addrbook()
{
...
}

...
};

ErrorCode
OS_GDAddrBook_Alloc(GDAddrBook** gppAddrbook)
{
if (!gppAddrbook)
return E_INVALIDARG;

os_addrbook* pAddrBook = new os_addrbook;
if (pAddrBook)
{
*gppAddrbook = (GDAddrBook*)pAddrBook;
return ERROR_SUCCESS;
}

return E_OUTOFMEMORY;
}



And finally here is a sample app :

GDAddrBook* pAddrBook = NULL;
if (!GDAddrBook_Alloc(&pAddrBook) &&
!GDAddrBook_Init(pAddrBook, 0) )
{
...
}

GDObject_Release(pAddrBook);


So when user call GDAddrBook_Alloc a structure(os_addrbook) inheriting
from the C GDObject structure is allocated and casted into a
GDAddrBook*( typedef for a GDObject*) and then returned to caller.
Then when GDObject_Release is called delete is called on the pointer
casted back to a GDObject*

In this case can delete work knowing that GDObject is actually a C
structure and thus there is no virtual destructor ?

So to sum up :

I allocate an object inheriting from GDObject(C struct) -> returned as a
GDAddrBook*(typedef for a GDObject*) to caller and then when I call
GDObject_Release pointer is cast to GDObject* and then delete is called
on it.

Can it work ?
 
R

red floyd

Hi,

I ma developping a small framework exporting C functions but behind the
scene I am manipulating C++ objects. Memory management relies on
reference count and the base C "object" is called GDObject and is
actually a structure with a counter.

///////////////////////////////////////////////////////
// Public declaration for GDObject used to handle "object" life
__BEGIN_C_DECLS

typedef struct _GDObject GDObject;

typedef struct _GDObject
{
[redacted]

Can it work ?

It might, but it might nott. Any identifier with a leading underscore
followed by
an upper-case letter is reserved to the implementation -- you may NOT
use it for your own
purposes. Therefore, you may be stepping on implementation defined
symbols here.
 
M

Marcel Müller

Mosfet said:
I allocate an object inheriting from GDObject(C struct) -> returned as a
GDAddrBook*(typedef for a GDObject*) to caller and then when I call
GDObject_Release pointer is cast to GDObject* and then delete is called
on it.

Can it work ?

This will never work as expected. Deleting a pointer to a POD type never
invokes any destructor.
You have to do an explicit down cast to a C++ type before invoking
delete on a non POD type though a base class pointer. But since your
reference counting functions do not know the actual type, there will be
no C compatible solution.

However, the method _GDObject_Release may invoke a static deleter
function. In this function you might do the cast to the C++ type. In
fact you need the opposite function of OS_GDAddrBook_Alloc, i.e.
OS_GDAddrBook_Destroy.
Of course, if your objects become polymorphic you need a common base
class with a virtual destructor and the deallocation function only casts
to this base class. Everything else is done by dispatch table.

What I wonder a bit is, that the structure of GDObject is part of the
public interface. Since it is very dangerous to modify cRefs from
outside your library and even read access is only reliable if the result
is 0 or 1, it should be a hidden part of your implementation.

Normally I would recommend that a common base class with a protected
constructor and an protected virtual destructor has cRefs as a private
field initialized to 1 at construction. The class then provides public
static functions for incrementing/decrementing the reference count. This
would encapsulate most of the lifetime management safely.


Marcel
 
C

Chris M. Thomasson

Mosfet said:
Hi,

I ma developping a small framework exporting C functions but behind the
scene I am manipulating C++ objects. Memory management relies on reference
count and the base C "object" is called GDObject and is actually a
structure with a counter.


///////////////////////////////////////////////////////
// Public declaration for GDObject used to handle "object" life
__BEGIN_C_DECLS

typedef struct _GDObject GDObject;

typedef struct _GDObject
{
long cRefs;
} GDObject;

#define GDObject_AddRef(x) _GDObject_AddRef((GDObject*) (x));
#define GDObject_Release(x) _GDObject_Release((GDObject*) (x));

GYNOID_API long
_GDObject_AddRef(GDObject* gdObject);

GYNOID_API long
_GDObject_Release(GDObject* gdObject);

__END_C_DECLS

///////////////////////////////////////////////////////
//Private implementation of GDObject :
...
long
_GDObject_Release(GDObject* gdObject)
{
LONG lRef;

if (!gdObject )
return 0;

if (gdObject->cRefs == 1)
{
lRef = 0;
gdObject->cRefs = 0;
}
else
{
lRef = InterlockedDecrement(&(gdObject->cRefs));
}

if (lRef == 0)
{
delete gdObject;
}

return (ULONG)lRef;
}
[..]

One quick question, what type of thread-safety level does your reference
counting enforce? I am guessing normal/basic, just like `boost:shared_ptr',
am I right? Here is a lively discussion on the difference between
normal/basic and strong thread-safety:

http://groups.google.com/group/comp.programming.threads/browse_frm/thread/e5167941d32340c6
 
C

Chris M. Thomasson

[...]
One quick question, what type of thread-safety level does your reference
counting enforce? I am guessing normal/basic, just like
`boost:shared_ptr', am I right? Here is a lively discussion on the
difference between normal/basic and strong thread-safety:

http://groups.google.com/group/comp.programming.threads/browse_frm/thread/e5167941d32340c6

BTW, I give links to one of my strongly thread-safe reference counting
algorithms as:

http://appcore.home.comcast.net/vzoom/refcount


I moves to South Lake Tahoe since then and all the links are busted; my new
website is:


http://webpages.charter.net/appcore


Therefore, the link above is valid when entered as:


http://webpages.charter.net/appcore/vzoom/refcount


Sorry about that.




Here is another very slick strong refcount algorithm:

http://atomic-ptr-plus.sourceforge.net
 
C

Chris M. Thomasson

Chris M. Thomasson said:
[...]
One quick question, what type of thread-safety level does your reference
counting enforce? I am guessing normal/basic, just like
`boost:shared_ptr', am I right? Here is a lively discussion on the
difference between normal/basic and strong thread-safety:

http://groups.google.com/group/comp.programming.threads/browse_frm/thread/e5167941d32340c6

BTW, I give links to one of my strongly thread-safe reference counting
algorithms as:

http://appcore.home.comcast.net/vzoom/refcount


I moves to South Lake Tahoe since then and all the links are busted; my
new website is:


http://webpages.charter.net/appcore
[...]

There is also busted links to the following proxy garbage collector
algorithm:

http://home.comcast.net/~vzoom/demos/pc_sample.c



Here is working link:

http://webpages.charter.net/appcore/misc/pc_sample_h.html

http://webpages.charter.net/appcore/misc/pc_sample_c.html
 
B

Bart van Ingen Schenau

Mosfet said:
Hi,

I ma developping a small framework exporting C functions but behind
the scene I am manipulating C++ objects. Memory management relies on
reference count and the base C "object" is called GDObject and is
actually a structure with a counter.


///////////////////////////////////////////////////////
// Public declaration for GDObject used to handle "object" life
__BEGIN_C_DECLS

typedef struct _GDObject GDObject;

Names starting with an underscore or containing a double underscore are
reserved for use by the implementation. You are well-advised not to use
such names yourself.
typedef struct _GDObject
{
long cRefs;
} GDObject;

Why is this definition of _GDObject public?
Clients have no need of this information and for derived classes you
should provide a separate (C++) header.

So when user call GDAddrBook_Alloc a structure(os_addrbook) inheriting
from the C GDObject structure is allocated and casted into a
GDAddrBook*( typedef for a GDObject*) and then returned to caller.
Then when GDObject_Release is called delete is called on the pointer
casted back to a GDObject*

In this case can delete work knowing that GDObject is actually a C
structure and thus there is no virtual destructor ?

No, that results in Undefined Behaviour.
If you want GDObject_Release to work correctly, then GDObject must have
a virtual destructor.
For clients written in C, this does not matter, because they only see a
pointer and it does not matter if that pointer points to a (C
compatible) POD struct or a non-POD class with a virtual destructor.
So to sum up :

I allocate an object inheriting from GDObject(C struct) -> returned as
a GDAddrBook*(typedef for a GDObject*) to caller and then when I call
GDObject_Release pointer is cast to GDObject* and then delete is
called on it.

Can it work ?

No. GDObject must cease to be a C struct and be given a virtual
destructor.

Bart v Ingen Schenau
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,042
Latest member
icassiem

Latest Threads

Top