Re: Constructing Derived in shell of Base <shudder>
On 07/13/2010 07:47 PM, Victor Bazarov wrote:
On 7/13/2010 2:23 PM, Francesco S. Carta wrote:
Stephen Howe <sjhoweATdialDOTpipexDOTcom>, on 13/07/2010 19:10:47, wrote:
Hi
For the code
struct Base { int j; virtual void f(); };
struct Derived : public Base { virtual void f(); };
void fooBar
{
Base b;
b.f(); // Base::f() invoked
b.~Base();
new (&b) Derived; // placement new => as same size, construct Derived
in shell of Base
b.f(); // Which f() is invoked, Base or Derived?
}
1) Is it well-defined? if not, why not?
2) If it is well defined, which f() is invoked? I think it is the
Derived f() but a case could be made for Base.
Well, as it is, strictly speaking, it simply doesn't compile (things
like missing declarations of both f(), missing parentheses after fooBar,
missing main()).
Then, if you tried to make something compilable out of it you would have
discovered that only Base::f() gets called - after all, b is an instance
of Base (during both calls of f()) and your code doesn't transform it to
a Derived - you're simply messing with its storage and I think your code
leads to UB.
More experienced people hanging around will give you better insights.
Well, "messing with its storage" should in this case be enough because
the expression 'new (&b) Derived' is supposed to *construct* the Derived
object in the memory that 'b' occupies. The act of construction of that
object *should* include setting up the virtual function resolution
mechanism properly for the call to 'b.f()' *if* 'b' were a reference.
Since 'b' isn't a reference, but a plain object, the compiler creates
the call to B::f when you write 'b.f()'.
Here is the fixed program, which works "as the OP expected":
#include <iostream>
#include <memory>
struct B { int j; virtual void f() { std::cout << "B::f\n"; } };
struct D : B { virtual void f() { std::cout << "D::f\n";} };
int main()
{
B b;
B& rb = b;
rb.f(); // B::f() invoked
b.~B();
The following line is horrendous, it assumes that the derived class D is
the same size as class B
new (&b) D; // create D in shell of B
rb.f(); // D::f() invoked
}
V
IMHO that is just as horrendous, what happens if changes cause the size
of D to become larger than the size of B? Memory corruption, that's
what, and then all bets are off.
may I suggest the following
//---------------------
struct B { int j; virtual void f() { std::cout << "B::f\n"; } virtual
~B() {} };
struct D : B { virtual void f() { std::cout << "D::f\n";} };
int main()
{
char obj[sizeof(D)];
B *p = new (obj) B;
p->f(); // B::f() involked
p->~B();
B *p = new (obj) D;
p->f(); // D::f() involked
p->~B(); // D Destructor will be called
}
//---------------------
This still requires care, as before creating an object with new, any
previously created object should be destroyed. Worse still the object
will not automatically be destroyed when it goes out of scope. Therefore
IMHO this sort of functionality should be encapsulated within a class
that ensures things are handled safely.
cpp4ever