问题
Has anyone here ever used C++\'s \"placement new\"? If so, what for? It looks to me like it would only be useful on memory-mapped hardware.
回答1:
Placement new allows you to construct an object in memory that's already allocated.
You may want to do this for optimization when you need to construct multiple instances of an object, and it is faster not to re-allocate memory each time you need a new instance. Instead, it might be more efficient to perform a single allocation for a chunk of memory that can hold multiple objects, even though you don't want to use all of it at once.
DevX gives a good example:
Standard C++ also supports placement new operator, which constructs an object on a pre-allocated buffer. This is useful when building a memory pool, a garbage collector or simply when performance and exception safety are paramount (there's no danger of allocation failure since the memory has already been allocated, and constructing an object on a pre-allocated buffer takes less time):
char *buf = new char[sizeof(string)]; // pre-allocated buffer
string *p = new (buf) string("hi"); // placement new
string *q = new string("hi"); // ordinary heap allocation
You may also want to be sure there can be no allocation failure at a certain part of critical code (for instance, in code executed by a pacemaker). In that case you would want to allocate memory earlier, then use placement new within the critical section.
Deallocation in placement new
You should not deallocate every object that is using the memory buffer. Instead you should delete[] only the original buffer. You would have to then call the destructors of your classes manually. For a good suggestion on this, please see Stroustrup's FAQ on: Is there a "placement delete"?
回答2:
We use it with custom memory pools. Just a sketch:
class Pool {
public:
Pool() { /* implementation details irrelevant */ };
virtual ~Pool() { /* ditto */ };
virtual void *allocate(size_t);
virtual void deallocate(void *);
static Pool::misc_pool() { return misc_pool_p; /* global MiscPool for general use */ }
};
class ClusterPool : public Pool { /* ... */ };
class FastPool : public Pool { /* ... */ };
class MapPool : public Pool { /* ... */ };
class MiscPool : public Pool { /* ... */ };
// elsewhere...
void *pnew_new(size_t size)
{
return Pool::misc_pool()->allocate(size);
}
void *pnew_new(size_t size, Pool *pool_p)
{
if (!pool_p) {
return Pool::misc_pool()->allocate(size);
}
else {
return pool_p->allocate(size);
}
}
void pnew_delete(void *p)
{
Pool *hp = Pool::find_pool(p);
// note: if p == 0, then Pool::find_pool(p) will return 0.
if (hp) {
hp->deallocate(p);
}
}
// elsewhere...
class Obj {
public:
// misc ctors, dtors, etc.
// just a sampling of new/del operators
void *operator new(size_t s) { return pnew_new(s); }
void *operator new(size_t s, Pool *hp) { return pnew_new(s, hp); }
void operator delete(void *dp) { pnew_delete(dp); }
void operator delete(void *dp, Pool*) { pnew_delete(dp); }
void *operator new[](size_t s) { return pnew_new(s); }
void *operator new[](size_t s, Pool* hp) { return pnew_new(s, hp); }
void operator delete[](void *dp) { pnew_delete(dp); }
void operator delete[](void *dp, Pool*) { pnew_delete(dp); }
};
// elsewhere...
ClusterPool *cp = new ClusterPool(arg1, arg2, ...);
Obj *new_obj = new (cp) Obj(arg_a, arg_b, ...);
Now you can cluster objects together in a single memory arena, select an allocator which is very fast but does no deallocation, use memory mapping, and any other semantic you wish to impose by choosing the pool and passing it as an argument to an object's placement new operator.
回答3:
It's useful if you want to separate allocation from initialization. STL uses placement new to create container elements.
回答4:
I've used it in real-time programming. We typically don't want to perform any dynamic allocation (or deallocation) after the system starts up, because there's no guarantee how long that is going to take.
What I can do is preallocate a large chunk of memory (large enough to hold any amount of whatever that the class may require). Then, once I figure out at runtime how to construct the things, placement new can be used to construct objects right where I want them. One situation I know I used it in was to help create a heterogeneous circular buffer.
It's certainly not for the faint of heart, but that's why they make the syntax for it kinda gnarly.
回答5:
I've used it to construct objects allocated on the stack via alloca().
shameless plug: I blogged about it here.
回答6:
Head Geek: BINGO! You got it totally - that's exactly what it's perfect for. In many embedded environments, external constraints and/or the overall use scenario forces the programmer to separate the allocation of an object from its initialization. Lumped together, C++ calls this "instantiation"; but whenever the constructor's action must be explicitly invoked WITHOUT dynamic or automatic allocation, placement new is the way to do it. It's also the perfect way to locate a global C++ object that is pinned to the address of a hardware component (memory-mapped I/O), or for any static object that, for whatever reason, must reside at a fixed address.
回答7:
I've used it to create a Variant class (i.e. an object that can represent a single value that can be one of a number of different types).
If all of the value-types supported by the Variant class are POD types (e.g. int, float, double, bool) then a tagged C-style union is sufficient, but if you want some of the value-types to be C++ objects (e.g. std::string), the C union feature won't do, as non-POD datatypes may not be declared as part of a union.
So instead I allocate a byte array that is big enough (e.g. sizeof(the_largest_data_type_I_support)) and use placement new to initialize the appropriate C++ object in that area when the Variant is set to hold a value of that type. (And placement delete beforehand when switching away from a different non-POD data type, of course)
回答8:
It's also useful when you want to re-initialize global or statically allocated structures.
The old C way was using memset()
to set all elements to 0. You cannot do that in C++ due to vtables and custom object constructors.
So I sometimes use the following
static Mystruct m;
for(...) {
// re-initialize the structure. Note the use of placement new
// and the extra parenthesis after Mystruct to force initialization.
new (&m) Mystruct();
// do-some work that modifies m's content.
}
回答9:
Placement new is also very useful when serialising (say with boost::serialization). In 10 years of c++ this is only the second case I've needed placement new for (third if you include interviews :) ).
回答10:
It is useful if you are building a kernel - where do you place the kernel code you read from disk or the pagetable? You need to know where to jump to.
Or in other, very rare circumstances such as when you have loads of allocated room and want to place a few structures behind each other. They can be packed this way without the need for the offsetof() operator. There are other tricks for that too, though.
I also believe some STL implementations make use of placement new, like std::vector. They allocate room for 2^n elements that way and don't need to always realloc.
回答11:
I think this has not been highlighted by any answer, but another good example and usage for the new placement is to reduce the memory fragmentation (by using memory pools). This is specially useful in embedded and high availability systems. In this last case it's specially important because for a system that has to run 24/365 days it's very important to have no fragmentation. This problem has nothing to do with memory leakage.
Even when a very good malloc implementation is used (or similar memory management function) it's very difficult to deal with fragmentation for a long time. At some point if you don't manage cleverly the memory reservation/release calls you could end up with a lot of small gaps that are difficult to reuse (assign to new reservations). So, one of the solutions that are used in this case is to use a memory pool to allocate before hand the memory for the application objects. After-wards each time you need memory for some object you just use the new placement to create a new object on the already reserved memory.
This way, once your application starts you already have all the needed memory reserved. All the new memory reservation/release goes to the allocated pools (you may have several pools, one for each different object class). No memory fragmentation happens in this case since there will no gaps and your system can run for very long periods (years) without suffering from fragmentation.
I saw this in practice specially for the VxWorks RTOS since its default memory allocation system suffers a lot from fragmentation. So allocating memory through the standard new/malloc method was basically prohibited in the project. All the memory reservations should go to a dedicated memory pool.
回答12:
It's actually kind of required to implement any kind of data structure that allocates more memory than minimally required for the number of elements inserted (i.e., anything other than a linked structure which allocates one node at a time).
Take containers like unordered_map
, vector
, or deque
. These all allocate more memory than is minimally required for the elements you've inserted so far to avoid requiring a heap allocation for every single insertion. Let's use vector
as the simplest example.
When you do:
vector<Foo> vec;
// Allocate memory for a thousand Foos:
vec.reserve(1000);
... that doesn't actually construct a thousand Foos. It simply allocates/reserves memory for them. If vector
did not use placement new here, it would be default-constructing Foos
all over the place as well as having to invoke their destructors even for elements you never even inserted in the first place.
Allocation != Construction, Freeing != Destruction
Just generally speaking to implement many data structures like the above, you cannot treat allocating memory and constructing elements as one indivisible thing, and you likewise cannot treat freeing memory and destroying elements as one indivisible thing.
There has to be a separation between these ideas to avoid superfluously invoking constructors and destructors unnecessarily left and right, and that's why the standard library separates the idea of std::allocator
(which doesn't construct or destroy elements when it allocates/frees memory*) away from the containers that use it which do manually construct elements using placement new and manually destroy elements using explicit invocations of destructors.
- I hate the design of
std::allocator
but that's a different subject I'll avoid ranting about. :-D
So anyway, I tend to use it a lot since I've written a number of general-purpose standard-compliant C++ containers that could not be built in terms of the existing ones. Included among them is a small vector implementation I built a couple decades ago to avoid heap allocations in common cases, and a memory-efficient trie (doesn't allocate one node at a time). In both cases I couldn't really implement them using the existing containers, and so I had to use placement new
to avoid superfluously invoking constructors and destructors on things unnecessary left and right.
Naturally if you ever work with custom allocators to allocate objects individually, like a free list, then you'd also generally want to use placement new
, like this (basic example which doesn't bother with exception-safety or RAII):
Foo* foo = new(free_list.allocate()) Foo(...);
...
foo->~Foo();
free_list.free(foo);
回答13:
I've used it for storing objects with memory mapped files.
The specific example was an image database which processed vey large numbers of large images (more than could fit in memory).
回答14:
I've seen it used as a slight performance hack for a "dynamic type" pointer (in the section "Under the Hood"):
But here is the tricky trick I used to get fast performance for small types: if the value being held can fit inside of a void*, I don't actually bother allocating a new object, I force it into the pointer itself using placement new.
回答15:
It's used by std::vector<>
because std::vector<>
typically allocates more memory than there are objects
in the vector<>
.
回答16:
I've used it to create objects based on memory containing messages received from the network.
回答17:
Generally, placement new is used to get rid of allocation cost of a 'normal new'.
Another scenario where I used it is a place where I wanted to have access to the pointer to an object that was still to be constructed, to implement a per-document singleton.
回答18:
It may be handy when using shared memory, among other uses... For example: http://www.boost.org/doc/libs/1_51_0/doc/html/interprocess/synchronization_mechanisms.html#interprocess.synchronization_mechanisms.conditions.conditions_anonymous_example
回答19:
The one place I've run across it is in containers which allocate a contiguous buffer and then fill it with objects as required. As mentioned, std::vector might do this, and I know some versions of MFC CArray and/or CList did this (because that's where I first ran across it). The buffer over-allocation method is a very useful optimization, and placement new is pretty much the only way to construct objects in that scenario. It is also used sometimes to construct objects in memory blocks allocated outside of your direct code.
I have used it in a similar capacity, although it doesn't come up often. It's a useful tool for the C++ toolbox, though.
回答20:
Script engines can use it in the native interface to allocate native objects from scripts. See Angelscript (www.angelcode.com/angelscript) for examples.
回答21:
See the fp.h file in the xll project at http://xll.codeplex.com It solves the "unwarranted chumminess with the compiler" issue for arrays that like to carry their dimensions around with them.
typedef struct _FP
{
unsigned short int rows;
unsigned short int columns;
double array[1]; /* Actually, array[rows][columns] */
} FP;
回答22:
Here is the killer use for the C++ in-place constructor: aligning to a cache line, as well as other powers of 2 boundaries. Here is my ultra-fast pointer alignment algorithm to any power of 2 boundaries with 5 or less single-cycle instructions:
/* Quickly aligns the given pointer to a power of two boundary IN BYTES.
@return An aligned pointer of typename T.
@brief Algorithm is a 2's compliment trick that works by masking off
the desired number in 2's compliment and adding them to the
pointer.
@param pointer The pointer to align.
@param boundary_byte_count The boundary byte count that must be an even
power of 2.
@warning Function does not check if the boundary is a power of 2! */
template <typename T = char>
inline T* AlignUp(void* pointer, uintptr_t boundary_byte_count) {
uintptr_t value = reinterpret_cast<uintptr_t>(pointer);
value += (((~value) + 1) & (boundary_byte_count - 1));
return reinterpret_cast<T*>(value);
}
struct Foo { Foo () {} };
char buffer[sizeof (Foo) + 64];
Foo* foo = new (AlignUp<Foo> (buffer, 64)) Foo ();
Now doesn't that just put a smile on your face (:-). I ♥♥♥ C++1x
来源:https://stackoverflow.com/questions/222557/what-uses-are-there-for-placement-new