Is it valid to initialize a base class with placement new?

J

Juha Nieminen

Assume I have a class Base, and then this:

struct Derived: public Base
{
AnotherClass member;
};

Also assume that I'm allocating an object of type 'Derived' with a
custom allocator and that, for whatever reason, I don't want to call the
constructor of 'Derived', but instead I want to build the object by
first making a placement new for the 'Base' and then another for the
'member'. (Yes, I know this might sound like a bit of a silly thing to
want, but just humor me, please.) Is it valid to do that?

The code would be something like this:

Derived* ptr = allocator.allocate(1);
new (static_cast<Base*>(ptr)) Base(baseParameters);
new (&ptr->member) AnotherClass(otherParameters);

This compiles (and doesn't crash), but is it valid?
 
P

peter koch

  Assume I have a class Base, and then this:

struct Derived: public Base
{
    AnotherClass member;

};

  Also assume that I'm allocating an object of type 'Derived' with a
custom allocator and that, for whatever reason, I don't want to call the
constructor of 'Derived', but instead I want to build the object by
first making a placement new for the 'Base' and then another for the
'member'. (Yes, I know this might sound like a bit of a silly thing to
want, but just humor me, please.) Is it valid to do that?

  The code would be something like this:

Derived* ptr = allocator.allocate(1);
new (static_cast<Base*>(ptr)) Base(baseParameters);
new (&ptr->member) AnotherClass(otherParameters);

  This compiles (and doesn't crash), but is it valid?

No. Not if member belongs to Derived.
In any case, you have not created a Derived object: To do this you
must call the contructor of derived. Perhaps it would make more sense
to describe what you are trying to accomplish?

/Peter
 
J

Juha Nieminen

peter said:
No. Not if member belongs to Derived.

How exactly are those lines I wrote above different from:

Derived* ptr = new Derived;

(assuming, of course, that the constructor of Derived passes the
'baseParameters' to Base and the 'otherParameters' to 'member', and that
'allocator' is the default allocator.)

In other words, does the 'new' do something that those three lines I
quoted above don't?
In any case, you have not created a Derived object: To do this you
must call the contructor of derived. Perhaps it would make more sense
to describe what you are trying to accomplish?

What I'm trying to accomplish is something which would be easily
solvable with variadic templates of C++0x, namely an easy way of
forwarding a bunch of values to the constructor of 'member' without
having to write a ton of constructors in the 'Derived' struct. (This
would be easy by putting a variadic template constructor in the
'Derived' struct, but until C++0x is ratified and implemented by
compilers, I have to resort to uglier tricks.)

Namely, I'm trying to pass a varied amount of parameters to the
constructor of 'member' (from within an outer template class), and with
placement new I can do that with a one-liner. With a proper construction
mechanism of 'Derived' I would have to write a big bunch of template
constructors for it, one for each possible amount of parameters. It
would of course be possible, but tedious.

(And no, calling the copy constructor or copy assignment of 'member'
is not an option in this case. If it would be, then that would be a
cleaner solution.)
 
C

crube

  How exactly are those lines I wrote above different from:

Derived* ptr = new Derived;

(assuming, of course, that the constructor of Derived passes the
'baseParameters' to Base and the 'otherParameters' to 'member', and that
'allocator' is the default allocator.)

  In other words, does the 'new' do something that those three lines I
quoted above don't?

Yes, it does. It calls a Derived object constructor. Besides that,
before the conctructor it "initializes" an object, i.e. sets up the
internal data such information about the base type (for dynamic_cast)
and vtable (in case you had virtual methods).

In your example you didn't created a Derived object at all so you've
got a Base type with all consequences of that. You only got memory
allocated.

Robert
 
J

James Kanze

How exactly are those lines I wrote above different from:
Derived* ptr = new Derived;

It calls the constructor of Derived.
(assuming, of course, that the constructor of Derived passes
the 'baseParameters' to Base and the 'otherParameters' to
'member', and that 'allocator' is the default allocator.)
In other words, does the 'new' do something that those three
lines I quoted above don't?

It calls the construtor of Derived. It does NOT call the
constructor of Base, nor does it call the constructor of member;
these are called by the constructor of Derived. (The
constructor of Derived may, and often does, other things as
well.)
 
J

Juha Nieminen

James said:
It calls the constructor of Derived.



It calls the construtor of Derived. It does NOT call the
constructor of Base, nor does it call the constructor of member;
these are called by the constructor of Derived. (The
constructor of Derived may, and often does, other things as
well.)

I still fail to see how the end result is different from what I wrote.

'Derived' has no explicit constructor, and thus only the
compiler-generated one. Also, 'Derived' has no member functions, and the
object of type 'Derived' is never used for anything else than to access
the base class function and the function of 'Member'. The destructor of
'Derived' is never called either (because I never call it explicitly nor
implicitly).

So basically the situation is that I have allocated space for an
object of type 'Base', with space for an object of type 'AnotherClass'
appended to it, and then I construct that 'Base' object with placement
new, as well as the 'member' object. The 'Derived' struct is only a
placeholder for this allocated memory, and never used for anything else.

So what is it that makes it illegal to do so?

(Why not simply have an object of type Base and another of type
AnotherClass and construct them normally? Because if Base is an empty
class, the compiler cannot perform empty base optimization.)
 
J

James Kanze

I still fail to see how the end result is different from what
I wrote.

Try it. Formally, it's undefined behavior, so the results may
vary. But suppose Base is a polymorphic class (otherwise, why
inherit). And suppose that you then assign your pointer to a
Base*, and use typeid on it. What do you get?
'Derived' has no explicit constructor, and thus only the
compiler-generated one.

And? The compiler generated one may still have something it
needs to do.
Also, 'Derived' has no member functions, and the object of
type 'Derived' is never used for anything else than to access
the base class function and the function of 'Member'. The
destructor of 'Derived' is never called either (because I
never call it explicitly nor implicitly).
So basically the situation is that I have allocated space for
an object of type 'Base', with space for an object of type
'AnotherClass' appended to it, and then I construct that
'Base' object with placement new, as well as the 'member'
object. The 'Derived' struct is only a placeholder for this
allocated memory, and never used for anything else.

In which case, there shouldn't be any problem, if they're only
place holders, and you never use the object as a Derived. But I
don't see any case where I'd want to do that; if I just wanted
to group objects, I'd use a struct.
So what is it that makes it illegal to do so?

The standard. And the way most compilers implement inheritence.
(Why not simply have an object of type Base and another of
type AnotherClass and construct them normally? Because if Base
is an empty class, the compiler cannot perform empty base
optimization.)

Which it might not do anyway?
 
J

Juha Nieminen

James said:
But I
don't see any case where I'd want to do that; if I just wanted
to group objects, I'd use a struct.

Except that if 'Base' is made a member variable of the struct, the
compiler won't be able to perform empty base class optimization (which
in this case is relevant).
 
T

Thomas J. Gritzan

Juha said:
Except that if 'Base' is made a member variable of the struct, the
compiler won't be able to perform empty base class optimization (which
in this case is relevant).

There is no empty base class optimization. You create an object of type
Base and an object of type AnotherClass. Neither is a base class of the
other.
Base would be a base class of Derived, but there is no object of type
Derived, because you don't create one. How should the compiler handle
empty base class optimization, when there is no object of a type with an
empty base class?

However, why don't you simple do:
new (static_cast<void*>(ptr)) Derived(parameters);
?
 
T

tony_in_da_uk

In other words, does the 'new' do something that those three lines I
quoted above don't?

Might do - up to the compiler - but can't see any reason given the
obvious non-polymorphic requirement.
What I'm trying to accomplish is something which would be easily
solvable with variadic templates of C++0x, namely an easy way of
forwarding a bunch of values to the constructor of 'member' without
having to write a ton of constructors in the 'Derived' struct. (This
would be easy by putting a variadic template constructor in the
'Derived' struct, but until C++0x is ratified and implemented by
compilers, I have to resort to uglier tricks.)

Namely, I'm trying to pass a varied amount of parameters to the
constructor of 'member' (from within an outer template class), and with
placement new I can do that with a one-liner. With a proper construction
mechanism of 'Derived' I would have to write a big bunch of template
constructors for it, one for each possible amount of parameters. It
would of course be possible, but tedious.

(And no, calling the copy constructor or copy assignment of 'member'
is not an option in this case. If it would be, then that would be a
cleaner solution.)

You've really got that many arguments? And/or doing this in enough
classes for it to become tedious? Impressive.

Just use a preprocessor macro (assuming you're using a capable
compiler)...

Tony
 
T

Triple-DES

  Assume I have a class Base, and then this:

struct Derived: public Base
{
    AnotherClass member;

};

[snip]

  The code would be something like this:

Derived* ptr = allocator.allocate(1);
new (static_cast<Base*>(ptr)) Base(baseParameters);
new (&ptr->member) AnotherClass(otherParameters);

  This compiles (and doesn't crash), but is it valid?

As others have pointed out, it is not valid (UB). If you want a
concrete refutation from the standard, see 12.7/1

"For an object of non-POD class type (clause 9), before the
constructor begins execution and after the destructor finishes
execution, referring to any nonstatic member or base class of the
object results in undefined behavior"

DP
 
J

Juha Nieminen

Thomas said:
However, why don't you simple do:
new (static_cast<void*>(ptr)) Derived(parameters);

Since the number of parameters can be anything, I would need to use a
variadic template to forward the parameters to the constructor of the
member variable. (I can't assume that the member variable supports
copy-constructing.)

Well, I ended up with a compromise: I created 7 constructors for
'Derived', taking 0-6 parameters. Hopefully up to 6 parameters will be
enough until the next C++ standard is widely implemented...
 
J

Juha Nieminen

Just use a preprocessor macro (assuming you're using a capable
compiler)...

You mean using a macro with a variable number of arguments? Is that
standard C++ yet?
 
T

Triple-DES

  Since the number of parameters can be anything, I would need to use a
variadic template to forward the parameters to the constructor of the
member variable. (I can't assume that the member variable supports
copy-constructing.)

  Well, I ended up with a compromise: I created 7 constructors for
'Derived', taking 0-6 parameters. Hopefully up to 6 parameters will be
enough until the next C++ standard is widely implemented...

What compiler are you currently using? GCC 4.3+ has variadic
templates, FWIW.

DP
 
A

anthonypon

  You mean using a macro with a variable number of arguments? Is that
standard C++ yet?

Nope, but it's very widely supported and better than undefined
behaviour.

Tony
 
J

Juha Nieminen

Triple-DES said:
What compiler are you currently using? GCC 4.3+ has variadic
templates, FWIW.

Yes, but I'm making a library I'm going to distribute, so I have to
stick to the current C++ standard...
 
J

Juha Nieminen

Alf said:
You have a bad design, and try to compensate with bad coding.

Instead fix the design.

In this case it's more a question of language limitation (fixed in
C++0x) than design.
 
J

Juha Nieminen

Alf said:
Have you considered replacing

Derived* ptr = allocator.allocate(1);
new (static_cast<Base*>(ptr)) Base(baseParameters);
new (&ptr->member) AnotherClass(otherParameters);

with e.g.

Derived d( new AnotherClass( otherParameters ) );

Just for example?

In this application in question that's precisely what I'm trying to
avoid. (The app in question would become completely moot.)

Ok, enough riddles. I made something which I think should work about
ok, and a webpage for it:

http://warp.povusers.org/RefPtr/

Probably not a novel idea, but it might be useful to someone, maybe.

("Why not simply use boost::shared_ptr?" Because boost::shared_ptr has
the size of two pointers and a payload of a whopping 44 bytes, at least
with linux gcc, making each (unshared) shared_ptr instance consume a
grand total of 56 bytes of RAM in a 32-bit system (2*4 bytes for the two
pointers, 44 bytes for the payload and 4 bytes for the clib memory
allocator payload), while my RefPtr consumes a grand total of 8 bytes
(sizeof(RefPtr) = 4, plus the intrusive reference counter). Sure, it's
not as versatile, but in many situations that doesn't matter.)
 
T

Thomas J. Gritzan

Juha said:
In this application in question that's precisely what I'm trying to
avoid. (The app in question would become completely moot.)

How about this:

struct Holder
{
unsigned char SomeSpace[sizeof(...)];
size_t Count;
};

Holder* ptr = allocator.allocate(1);
new (ptr) Holder(baseParameters);
new (ptr->SomeSpace) AnotherClass(otherParameters);
Ok, enough riddles. I made something which I think should work about
ok, and a webpage for it:

http://warp.povusers.org/RefPtr/

Probably not a novel idea, but it might be useful to someone, maybe.

Do you know this article? (by Andrei Alexandrescu)
http://www.informit.com/articles/article.aspx?p=31529&seqNum=6
On the following pages it describes some useful technics for smart
pointer implementations.
 
J

Juha Nieminen

Thomas said:
How about this:

struct Holder
{
unsigned char SomeSpace[sizeof(...)];
size_t Count;
};

Holder* ptr = allocator.allocate(1);
new (ptr) Holder(baseParameters);
new (ptr->SomeSpace) AnotherClass(otherParameters);

That might be one possible solution (at least to avoid having to write
all the constructors, or the variadic template constructor).
 

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,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top