Re: Constructing Derived in shell of Base <shudder>
On Jul 18, 8:34 am, James Kanze <james.ka...@gmail.com> wrote:
On Jul 16, 7:26 pm, Joshua Maurice <joshuamaur...@gmail.com> wrote:
Well, let me phrase the question like this. Is the following code
legal?
#include <new>
class foo
{
public:
virtual void bar() {}
};
int main()
{
foo * f = new foo;
foo * f2 = new (f) foo;
f2 -> bar();
}
I would never write code like this as a matter of style and
sanity, but I would still like to know as a matter of
understanding all of the standard and its nuances. Does that
phrase disallow this?
No. Other things might; I'd have to check. Suppose, for
example, and implementation dyanamically allocated a new copy of
the vtable for each instance of the variable. (I think this
would be legal.) I'm not sure about what happens when you reuse
the memory underlying an object with non-trivial destructors
without calling the destructor.
C++03 standard, 3.8 Object Lifetime / 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 o=
f 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 des=
tructor explicitly before the storage which the object occupies is reused o=
r 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 destruct=
or shall not be implicitly called and any program that depends on the side =
effects produced by the destructor has undefined behavior.
It seems like you can do such a thing. I would assume that failing to
call the destructor can result in a resource leak, which is generally
"bad", but which is not the standard's "undefined behavior".
As a separate question, I wonder why it's undefined behavior to rely
on the destructor's side effects in this case. I would imagine the
sensible thing would be "the destructor will not be implicitly
called". I would assume another open ended allowance to the
implementation with no specific implementation technique in mind. Or
perhaps, a debug implementation could use it?
From James
Kanze's reply, that might not be the intent, nor how it's commonly
understood.
Either I misstated my understanding, or you misunderstood what I
said. The expression sizeof(Type) returns the number of bytes
necessary to "contain" an instance of the object. Whether all
of those bytes are part of the object or not is a separate
question. And I think (but I'm not sure) that the
implementation is allowed to allocate other memory in the
constructor, and free it in the destructor. (Consider the oft
sited alternative to a vptr---the compiler allocates an entry in
a map somewhere.)
Presumably we're not talking about duck typing, right? That is, the
compiler understood types are immutable, and no new types may be
defined. In such a case, a call to a virtual function would be a
string lookup in some sort of map container, but that map container
would be compile time constant. I don't see the need to modify this
map every time an object of the relevant type is created or destroyed.
Could you please explain further?
Given an answer to the above question is 1 or 3, I have an additional
question: What motivated this allowance?
The motivation for any allowances here is to allow a maximum of
freedom to the implementation.
So, no particular implementation technique in particular? Ok. Again,
mostly my curiosity.
In no case, however, is there
any intent of allowing certain common idioms to fail. (Or
mayby... Suppose you derive from an empty PODS. Can you still
memcpy to it?)
Interesting question. I don't think I would ever use memcpy on sub-
objects, at least not until I see some evidence that all relevant
compilers will not break when doing so.