using delete expression on global operator new allocated memory

D

dizzy

Hi

I wonder if this code is standard conformant and should work on all
conformant implementations (for some type T):

1: void* mem = ::eek:perator new(sizeof(T));
2: T* p = new(mem) T(args...);
3: delete p;

line 2 I know it should be fine because global operator new should return
memory aligned for any type. The thing I wonder about is line 3. Should
this always work?

PS: the code might seem silly, it is needed because I need to decouple the
point of storage type used (which on 2 different codepaths can be on stack
or dynamic) from the point of actual initialization of the object and its
arguments (thus I need to use placement new); another solution I am aware
of whould be using some kind of "factory functors" (like the ones in boost)
but that would require for me to use template functions which is what I
want to avoid in the first place
 
J

James Kanze

I wonder if this code is standard conformant and should work on all
conformant implementations (for some type T):
1: void* mem = ::eek:perator new(sizeof(T));
2: T* p = new(mem) T(args...);
3: delete p;
line 2 I know it should be fine because global operator new
should return memory aligned for any type. The thing I wonder
about is line 3. Should this always work?

I think so, but I think the point is vague enough that I'd avoid
counting on it. The usual rule is to separate destruction and
deallocation anytime you've separated allocation and
initialization. In other words, you sould probably replace 3
with:

p->~T() ;
::eek:perator delete( p ) ;
PS: the code might seem silly, it is needed because I need to
decouple the point of storage type used (which on 2 different
codepaths can be on stack or dynamic) from the point of actual
initialization of the object and its arguments (thus I need to
use placement new);

I'm not sure I understand this. If the object is on the stack,
it will have been constructed where it was declared, and will be
destructed when it goes out of scope. So you can't use
placement new on it later, and you can't explicitly delete it
in any way.
 
D

dizzy

James said:
I think so, but I think the point is vague enough that I'd avoid
counting on it. The usual rule is to separate destruction and
deallocation anytime you've separated allocation and
initialization. In other words, you sould probably replace 3
with:

p->~T() ;
::eek:perator delete( p ) ;

That doesn't work the way I do things (explanation below).
I'm not sure I understand this. If the object is on the stack,
it will have been constructed where it was declared, and will be
destructed when it goes out of scope. So you can't use
placement new on it later, and you can't explicitly delete it
in any way.

But it exists in a possible call algorithm. So I will give more details
about the code.

Supose I have a hierarchy of user defined types, DerivedX (with X being from
1 to 3) inherited from Base. The contents of a DerivedX at some point it is
serialized somehow (say by first storing some kind of numeric value that
associates the type stored and then the contents of the type). When I do
deserialization obviously I need to read that value and then deserialize
the actual type that I know follows in the input stream by using a ctor of
the deserialized type that receives various arguments which arguments are
first deserialized. I cannot just make a ctor version for each
deserializable type that takes the stream type as argument and it will
construct by deserialization from the stream and I cannot do something like
overloading some operator<< on my stream types because there are many ways
the objects are serialized/deserialized and they don't all depend on the
stream type used (or making such stream types for each way would be
pointless). So each way to deserialize has an API that will do it and
considering one such way/API it should be able to provide 2 ways to do it:

- one way, more straightforward and OOP (especially since all are inherited
from a common base) is to have something like this:
auto_ptr<Node> load(InputStream& is);

Which obviously will read the type, will switch/case on it and then will
deserialize the arguments needed for the ctor and then do something like
return auto_ptr<Node>(new DerivedX(args...)); (where X will be dependent on
the read numeric value that associates the serialized type)

While fine from an OOP perspective this code fails in many other aspects
because it will "lose" the actual type it was dereferenced and will return
a (smart) pointer to the Base class thus forcing the callers to use some
method (say a Visitor aproach) to identify the type again and perform
actual work on the specific type. So then I said, I should have another API
that does not lose the type.

- the second way to perform object deserialization is to have a load like
this:
template<typename Function>
void load(InputStream& is, Function func);

In this second case, "load" will read again (from the stream) the numeric
value that identifies the serialized type, then again will deserialize the
arguments specific to this type and then will create locally (on stack) an
object of the specific type using the deserialized arguments and will call
func(thatobject) (so Function needs to have an operator() taking each
possible obeject type). This is like a callback algorithm but avoiding the
costs of using boost::function (or other such generic functor) because the
callback happens with normal function call resolved at compile time and
will also avoid a costly Visitor aproach to get the type lost from the
first "load" version.

In both "load" versions I need to read the numeric value, switch/case on it
and then deserialize the arguments specific to the type and then construct
the type from those arguments. Using the original question method I could
decouple the reading of arguments/construction from the actual object
storage without having the functions that do the actual read be templates
(which I want to avoid) but they could just be something like:
Derived0*
loadDerived0(InputStream& is, void* where) {
// deserialize specific Derived0 arguments
return new(where) Derived0(args...);
}

The callers of these functions can either come from the codepath with
the "functor" argument, in which case they allocated properly aligned
storage on the stack and they will call the functor with argument on the
initialized object and then get rid of it or they come from the codepath
with the load returning auto_ptr<Base> in which case they will allocate
memory aligned for anything with ::eek:perator new(sizeof(DerivedX)); and will
return an auto_ptr to it. And as such at some point auto_ptr will call
delete on the ::eek:perator new returned memory which I need to know if it is
valid code.
 

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,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top