问题
Does the C++ standard guarantee that uninitialized POD members retain their previous value after a placement new?
Or more precisely, will the following assert always be satisfied according to C++11?
#include <cstdlib>
#include <cassert>
struct Foo {
int alpha; // NOTE: Uninitialized
int beta = 0;
};
int main()
{
void* p = std::malloc(sizeof (Foo));
int i = some_random_integer();
static_cast<Foo*>(p)->alpha = i;
new (p) Foo;
assert(static_cast<Foo*>(p)->alpha == i);
}
Is the answer the same for C++03?
回答1:
Does the C++ standard guarantee that uninitialized POD members retain their previous value after a placement new?
Will the following assert always be satisfied according to C++11?
No.
Uninitialized data members have an indeterminate value, and this is not at all the same as saying that the underlying memory is left alone.
[C++11: 5.3.4/15]:
A new-expression that creates an object of typeT
initializes that object as follows:
- If the new-initializer is omitted, the object is default-initialized (8.5); if no initialization is performed, the object has indeterminate value.
- Otherwise, the new-initializer is interpreted according to the initialization rules of 8.5 for direct-initialization.
[C++11: 8.5/6]:
To default-initialize an object of typeT
means:
- if
T
is a (possibly cv-qualified) class type (Clause 9), the default constructor forT
is called (and the initialization is ill-formed ifT
has no accessible default constructor);- if
T
is an array type, each element is default-initialized;- otherwise, no initialization is performed.
[C++11: 12.1/6]:
A default constructor that is defaulted and not defined as deleted is implicitly defined when it is odr-used (3.2) to create an object of its class type (1.8) or when it is explicitly defaulted after its first declaration. The implicitly-defined default constructor performs the set of initializations of the class that would be performed by a user-written default constructor for that class with no ctor-initializer (12.6.2) and an empty compound-statement.
[C++11: 12.6.2/8]:
In a non-delegating constructor, if a given non-static data member or base class is not designated by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer) and the entity is not a virtual base class of an abstract class (10.4), then
- if the entity is a non-static data member that has a brace-or-equal-initializer, the entity is initialized as specified in 8.5;
- otherwise, if the entity is a variant member (9.5), no initialization is performed;
- otherwise, the entity is default-initialized (8.5).
(NB. the first option in 12.6.2/8
is how your member beta
is handled)
[C++11: 8.5/6]:
To default-initialize an object of typeT
means:
- if
T
is a (possibly cv-qualified) class type (Clause 9), the default constructor forT
is called (and the initialization is ill-formed ifT
has no accessible default constructor);- if
T
is an array type, each element is default-initialized;- otherwise, no initialization is performed.
[C++11: 8.5/11]:
If no initializer is specified for an object, the object is default-initialized; if no initialization is performed, an object with automatic or dynamic storage duration has indeterminate value.
A compiler could choose to zero-out (or otherwise alter) the underlying memory during allocation. For example, Visual Studio in debug mode is known to write recognisable values such as 0xDEADBEEF
into memory to aid debugging; in this case, you're likely to see 0xCDCDCDCD
which they use to mean "clean memory" (reference).
Will it, in this case? I don't know. I don't think that we can know.
What we do know is that C++ doesn't prohibit it, and I believe that brings us to the conclusion of this answer. :)
Is the answer the same for C++03?
Yes, though through slightly different logic:
[C++03: 5.3.4/15]:
A new-expression that creates an object of typeT
initializes that object as follows:
- If the new-initializer is omitted:
- If
T
is a (possibly cv-qualified) non-POD class type (or array thereof), the object is default-initialized (8.5). IfT
is a const-qualified type, the underlying class type shall have a user-declared default constructor.- Otherwise, the object created has indeterminate value. If
T
is a const-qualified type, or a (possibly cv-qualified) POD class type (or array thereof) containing (directly or indirectly) a member of const-qualified type, the program is ill-formed;- If the new-initializer is of the form
()
, the item is value-initialized (8.5);- If the new-initializer is of the form
(expression-list)
andT
is a class type, the appropriate constructor is called, usingexpression-list
as the arguments (8.5);- If the new-initializer is of the form
(expression-list)
andT
is an arithmetic, enumeration, pointer, or pointer-to-member type andexpression-list
comprises exactly one expression, then the object is initialized to the (possibly converted) value of the expression (8.5);- Otherwise the new-expression is ill-formed.
Now, all that was my strict interpretation of the rules of initialisation.
Speaking practically, I think you're probably correct in seeing a potential conflict with the definition of placement operator new
syntax:
[C++11: 18.6.1/3]:
Remarks: Intentionally performs no other action.
An example that follows explains that placement new
"can be useful for constructing an object at a known address".
However, it doesn't actually talk about the common use of constructing an object at a known address without mungling the values that were already there, but the phrase "performs no other action" does suggest that the intention is that your "indeterminate value" be whatever was in memory previously.
Alternatively, it may simply prohibit the operator itself from taking any action, leaving the allocator free to. It does seem to me that the important point the standard trying to make is that no new memory is allocated.
Regardless, accessing this data invokes undefined behaviour:
[C++11: 4.1/1]:
A glvalue (3.10) of a non-function, non-array typeT
can be converted to a prvalue. IfT
is an incomplete type, a program that necessitates this conversion is ill-formed. If the object to which the glvalue refers is not an object of typeT
and is not an object of a type derived fromT
, or if the object is uninitialized, a program that necessitates this conversion has undefined behavior. IfT
is a non-class type, the type of the prvalue is the cv-unqualified version ofT
. Otherwise, the type of the prvalue isT
.
So it doesn't really matter: you couldn't compliantly observe the original value anyway.
回答2:
C++11 12.6.2/8 "Initializing bases and members" says:
In a non-delegating constructor, if a given non-static data member or base class is not designated by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer) and the entity is not a virtual base class of an abstract class (10.4), then
- if the entity is a non-static data member that has a brace-or-equal-initializer, the entity is initialized as specified in 8.5;
- otherwise, if the entity is a variant member (9.5), no initialization is performed;
- otherwise, the entity is default-initialized (8.5).
Default initialization on an int
does nothing (8.5/6 "Initializers"):
To default-initialize an object of type T means:
- if T is a (possibly cv-qualified) class type (Clause 9), the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);
- if T is an array type, each element is default-initialized;
- otherwise, no initialization is performed.
So the member alpha
should be left alone.
来源:https://stackoverflow.com/questions/14659752/placement-new-and-uninitialized-pod-members