I'm writing a memory manager for my VM in C++. Well, more exactly the VM instructions will be compiled into C++ with an embedded memory manager. I'm much more comfortable in handling C, but now I do need native support of exception handling, pretty much the only reason I'm using C++.
Both C and C++ have the strict aliasing rule that two objects of incompatible types shall not overlap, with a small exception in C for unions. But to define the behaviour of memory allocation functions such as malloc
, calloc
, alloca
etc., the C standard has the following paragraph.
6.5-6 The effective type of an object for an access to its stored value is the declared type of the object, if any. Allocated objects have no declared type. If a value is stored into an object having no declared type through an lvalue having a type that is not a character type, then the type of the lvalue becomes the effective type of the object for that access and for subsequent accesses that do not modify the stored value. If a value is copied into an object having no declared type using
memcpy
ormemmove
, or is copied as an array of character type, then the effective type of the modified object for that access and for subsequent accesses that do not modify the value is the effective type of the object from which the value is copied, if it has one. For all other accesses to an object having no declared type, the effective type of the object is simply the type of the lvalue used for the access.
This effectively makes using raw allocated memory for any type a well defined behaviour in C. I tried to find a similar paragraph in the C++ standard document but could not find one. I think C++ has a different approach in this regard. What is the C++ equivalent of an 'allocated object having no declared type' in C, and how does the C++ standard define it?
For C++, this is is described in Object lifetime [object.life]; in particular:
Lifetime continues until the storage is reused or the object is destructed:
This has the rather odd implication that unused allocated storage (returned from
operator new
) contains an object of every trivial type that fits in that block of storage, at least until the block of storage is used. But then, the standard cares more about getting the treatment of non-trivial types right than this minor wart.I think "an allocated object with no declared type" is effectively allocated storage that is not yet initialised and no object has yet been created in that memory space. From the global allocation function
::operator new(std::size_t)
and family (§3.7.4/2);§3.7.4.1/2
The creation of an object, whether it is automatic or dynamic, is governed by two stages, the allocation itself and then the construction of the object in that space.
§3.8/1
Correspondingly;
C++ WD n4527.
I think, the approach of C++ in this regard can be summarized as: "
malloc()
is evil. Usenew
instead. There is no such thing as an object without a declared type." Of course, C++ implementations need to define the globaloperator new()
, which is basically the C++ version ofmalloc()
(and which can be provided by the user). The very existence of this operator proves that there is something like objects without a declared type in C++, but the standard won't admit it.If I were you, I'd take the pragmatic approach. Both the global
operator new()
andmalloc()
are available in C++, so any implementation must be able to use their return values sensibly. Especiallymalloc()
will behave identical in C and C++. So, just handle these untyped objects the same way as you would handle them in C, and you should be fine.I'm not sure such an analog exists, or is needed in C++. To allocate memory you can do one of three things
This will allocate
sizeof(Foo)
amount of memory on the stack. The size is known at compile-time, which is how the compiler knows how much space to allocate. The same is true of arrays, etc.The other option is
This will allocate from the heap, but again
sizeof(Foo)
must be known, but this allows allocation at run-time.The third option, as @BasileStarynkevitch mentions, is placement new
You can see that all of these allocation mechanisms in C++ require knowledge of the type that you are allocating space for.
While it is possible to use
malloc
, etc in C++, it is frowned upon as it goes against the grain for typical C++ semantics. You can see a discussion of a similar question. The other mechanisms of allocating "raw" memory are hacky. For exampleThis will allocate
size
number of bytes.