using placement new to re-initialize part of an object

  • Thread starter removeps-generic
  • Start date
R

removeps-generic

Hi. I'm using placement new to re-initialize part of an object. Is
this OK?

struct BaseImp
{
All& r_all;
BaseImp(All& all) : r_all(all)
};

struct Calc::Imp : public BaseImp
{
int d_x;
Imp(All& all, int x) : BaseImp(all), d_x(x) { }
}

void Calc::somefunction(All& newall)
{
assert(typeid(*d_imp) == typeid(Imp));
new (d_imp) BaseImp(newall);
}

This way, d_imp->d_x is unchanged, but d_imp->r_all is changed.

What does the standard say about this?

Thanks.
 
T

Thomas Tutone

Hi. I'm using placement new to re-initialize part of an object. Is
this OK?

No, at least not the way you're doing it.

Also, next time please post compilable code. Remember, what we do is
cut and paste your code and then try to compile it. The numerous
syntax errors are a distraction from your question.

Add:

#include <cassert>
struct BaseImp
{
All& r_all;
BaseImp(All& all) : r_all(all)

You omitted:

{}
};

struct Calc::Imp : public BaseImp

What is Calc? Is it a class that has Imp as a subclass? Or is it a
namespace? If the latter, you can't define Imp this way - you must do
so as:

namespace Calc {
struct Imp : public BaseImp

etc.
{
int d_x;
Imp(All& all, int x) : BaseImp(all), d_x(x) { }
}

You omitted the semicolon after the closing brace.
void Calc::somefunction(All& newall)
{
assert(typeid(*d_imp) == typeid(Imp));

What the heck is d_imp?
new (d_imp) BaseImp(newall);
}

I've probably missed several more errors.
This way, d_imp->d_x is unchanged, but d_imp->r_all is changed.

What makes you think that d_x is unchanged?

What does the standard say about this?

This goes by the technical term of "a hack." It causes undefined
behavior, because an existing object never has its destructor called,
but you are then reusing the memory where that undestructed object
sits. Moreover, it looks like you now intend to use the object as an
Impl, even though you just created a BaseImp. That too would be
undefined behavior.

There are much better, less-hackish ways of doing this, but if you
insist on using placement new, you're going to at least have to call
the destructor of the object before calling placement new, and then
you're going to have to use the object you then create, not a different
object.

Wait a second - this is a joke, right? Okay, you got me. The problem
is that people on this newsgroups really do post code like this, so
it's hard to tell when they're kidding.

Best regards,

Tom
 
K

Kai-Uwe Bux

Thomas said:
No, at least not the way you're doing it.

Also, next time please post compilable code. Remember, what we do is
cut and paste your code and then try to compile it. The numerous
syntax errors are a distraction from your question.

Add:

#include <cassert>


You omitted:

{}


What is Calc? Is it a class that has Imp as a subclass? Or is it a
namespace? If the latter, you can't define Imp this way - you must do
so as:

namespace Calc {
struct Imp : public BaseImp

etc.


You omitted the semicolon after the closing brace.


What the heck is d_imp?


I've probably missed several more errors.


What makes you think that d_x is unchanged?



This goes by the technical term of "a hack." It causes undefined
behavior, because an existing object never has its destructor called,
but you are then reusing the memory where that undestructed object
sits.

Where do you find that this is undefined behavior? From the standard:

[3.8/4] A program may end the lifetime of any object by reusing the
storage which the object occupies or by explicitly calling the destructor
for an object of a class type with a non-trivial destructor. For an object
of a class type with a non-trivial destructor, the program is not required
to call the destructor explicitly before the storage which the object
occupies is reused or released; however, if there is no explicit call to
the destructor or if a delete-expression (5.3.5) is not used to release
the storage, the destructor shall not be implicitly called and any program
that depends on the side effects produced by the destructor has undefined
behavior.

The way I read this says: you can reuse the memory that an object occupies
without calling its destructor, provided you are not relying on side
effects of a possibly non-trivial destructor in the program.

Moreover, it looks like you now intend to use the object as an
Impl, even though you just created a BaseImp. That too would be
undefined behavior.

This maybe true. I am not sure though. The way I see it, the OP reuses the
memory occupied by a BaseImp subobject inside an Impl object and constructs
a new BaseImp object in there. It is quite possible that this invalidates
the ambient Impl object, but I don't know what the standard says about
this. Do you happen to know a reference?

There are much better, less-hackish ways of doing this,
Agreed.

but if you
insist on using placement new, you're going to at least have to call
the destructor of the object before calling placement new, and then
you're going to have to use the object you then create, not a different
object.

I agree, that would be safer.
Wait a second - this is a joke, right? Okay, you got me. The problem
is that people on this newsgroups really do post code like this, so
it's hard to tell when they're kidding.

I, too, hope it's a joke.


Best

Kai-Uwe Bux
 
T

Thomas Tutone

Kai-Uwe Bux wrote:

You raise good points. Let me respond as best I can.
This goes by the technical term of "a hack." It causes undefined
behavior, because an existing object never has its destructor called,
but you are then reusing the memory where that undestructed object
sits.

Where do you find that this is undefined behavior? From the standard:

[3.8/4] A program may end the lifetime of any object by reusing the
storage which the object occupies or by explicitly calling the destructor
for an object of a class type with a non-trivial destructor. For an object
of a class type with a non-trivial destructor, the program is not required
to call the destructor explicitly before the storage which the object
occupies is reused or released; however, if there is no explicit call to
the destructor or if a delete-expression (5.3.5) is not used to release
the storage, the destructor shall not be implicitly called and any program
that depends on the side effects produced by the destructor has undefined
behavior.

The way I read this says: you can reuse the memory that an object occupies
without calling its destructor, provided you are not relying on side
effects of a possibly non-trivial destructor in the program.

This just may be an example of my saying the glass is half-empty, and
you saying the glass is half full. The section you quote says, in
effect, if an object has a non-trivial destructor, then the failure to
call the destructor is undefined unless there is nothing that relies,
expressly or implicitly, on the behavior of that destructor. To me,
that means undefined behavior. You say, quite reasonably, that so long
as an object has a trivial destructor, or at least there is nothing
relying on the behavior of the structure, doing this is well defined.
To me, those constraints are too great - they are enough for me to say,
"because using this idiom will often result in undefined behavior, one
should not use this idiom." Among other things, I would think that the
compiler may make certain aliasing assumptions when it optimizes that
will turn out not to be true.

This maybe true. I am not sure though. The way I see it, the OP reuses the
memory occupied by a BaseImp subobject inside an Impl object and constructs
a new BaseImp object in there. It is quite possible that this invalidates
the ambient Impl object, but I don't know what the standard says about
this. Do you happen to know a reference?

No offhand. But consider: the compiler may add padding to BaseImp and
may not add that same padding to to Impl. The compiler could
conceivably overwrite that padding. But perhaps I am wrong about this.
But in any case, here's an analogy - the OP is creating the moral
equivalent of the following:

union { BaseImpl base; Impl impl };

Doing that is generally undefined except for POD. (Perhaps I am
overstating that a bit, but you know what I mean). At the very least,
it illustrates that this is a hack.

Best regards,

Tom
 
R

removeps-generic

This maybe true. I am not sure though. The way I see it, the OP reuses the
memory occupied by a BaseImp subobject inside an Impl object and constructs
a new BaseImp object in there. It is quite possible that this invalidates
the ambient Impl object, but I don't know what the standard says about
this. Do you happen to know a reference?

What do you mean by "the ambient Impl object"?


Such as? Using pointers?


Thomas, sorry for not posting compilable code. I just wanted to get
the point across, not intending for anyone to compile code. But here
is compilable code.

struct B
{
int& r_b;
B(int& b) : r_b(b) { }
};

struct D : public B
{
int d_d;
D(int& b, int d) : B(b), d_d(d) { }
};

int main()
{
int x=1, y=2;
D * d = new D(x,1);
new (d) B(y);
}

This is a way of re-assigning a reference. The reason for doing this
that I'm implementing a swap function (it's not a joke), and I'd like
to reset a reference. Constructing a brand new D object is expensive.

I suppose one should call the destructor to be correct prioir to
calling placement new

D * d = new D(x,1);
d->B::~B();
new (d) B(y);

But before one does this one should make sure that the destructor of B
is not virtual.

In any case, in .Net 2003, when debugging, and I'm on "new D(x, 1)"
line, when I step over (with F10), the debugger skips the "d->B::~B()"
call. This means there was no code generated for the destructor call.
Of course, this issue is specific to MSVC, but I suspect all compilers
would behave the same way.
 
T

Thomas Tutone

Such as? Using pointers?

Based on what you want to do, yes. Possibly smart pointers rather than
raw pointers. Or perhaps you could create a small "reseatable
reference" class that uses a pointer internally, but has a
reference-like interface.
Thomas, sorry for not posting compilable code. I just wanted to get
the point across, not intending for anyone to compile code. But here
is compilable code.

struct B
{
int& r_b;
B(int& b) : r_b(b) { }
};

struct D : public B
{
int d_d;
D(int& b, int d) : B(b), d_d(d) { }
};

int main()
{
int x=1, y=2;
D * d = new D(x,1);
new (d) B(y);
}
Thanks.


This is a way of re-assigning a reference. The reason for doing this
that I'm implementing a swap function (it's not a joke), and I'd like
to reset a reference. Constructing a brand new D object is expensive.

I suppose one should call the destructor to be correct prioir to
calling placement new

D * d = new D(x,1);
d->B::~B();
new (d) B(y);

But before one does this one should make sure that the destructor of B
is not virtual.

In any case, in .Net 2003, when debugging, and I'm on "new D(x, 1)"
line, when I step over (with F10), the debugger skips the "d->B::~B()"
call. This means there was no code generated for the destructor call.
Of course, this issue is specific to MSVC, but I suspect all compilers
would behave the same way.

I suspect you're right. As Kai-Uwe Bux pointed out, the behavior _may_
be well defined if the class has a trivial destructor. A class that
contains nothing but POD and a reference presumably has a trivial
destructor that essentially is a no-op.

But I do think you're better off using pointers, and thereby make your
class objects assignable, rather than trying to subvert the rule that
references can't be reseated.

Best regards,

Tom
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top