Placement syntaxIn the C++ programming language, placement syntax allows programmers to explicitly specify the memory management of individual objects — i.e. their "placement" in memory. Normally, when an object is created dynamically, an allocation function is invoked in such a way that it will both allocate memory for the object, and initialize the object within the newly allocated memory. The placement syntax allows the programmer to supply additional arguments to the allocation function. A common use is to supply a pointer to a suitable region of storage where the object can be initialized, thus separating memory allocation from object construction.[citation needed] The "placement" versions of the Any HistoryIn earlier versions of C++, there was no such thing as placement new; instead, developers used explicit assignment to ExpressionsThe Standard C++ syntax for a non-placement
The placement syntax adds an expression list immediately after the
FunctionsThe placement new functions are overloads of the non-placement new functions. The declaration of the non-placement new functions, for non-array and array void* operator new(std::size_t) throw(std::bad_alloc);
void* operator new[](std::size_t) throw(std::bad_alloc);
The Standard C++ library provides two placement overloads each for these functions. Their declarations are:[7][8] void* operator new(std::size_t, const std::nothrow_t&) throw();
void* operator new(std::size_t, void*) throw();
void* operator new[](std::size_t, const std::nothrow_t&) throw();
void* operator new[](std::size_t, void*) throw();
In all of the overloads, the first parameter to the There are also placement delete functions. They are overloaded versions of the non-placement delete functions. The non-placement delete functions are declared as:[7][8] void operator delete(void*) throw();
void operator delete[](void*) throw();
The Standard C++ library provides two placement overloads each for these functions. Their declarations are:[7][8] void operator delete(void*, const std::nothrow_t&) throw();
void operator delete(void*, void*) throw();
void operator delete[](void*, const std::nothrow_t&) throw();
void operator delete[](void*, void*) throw();
In all of the overloads, the first parameter to the For both the new and the delete functions, the functions are global, are not in any namespace, and do not have static linkage.[2] UsePlacement syntax has four main uses: default placement, preventing exceptions, custom allocators, and debugging. Default placementThe placement overloads of void* operator new(std::size_t, void* p) throw() { return p; }
void* operator new[](std::size_t, void* p) throw() { return p; }
void operator delete(void*, void*) throw() { }
void operator delete[](void*, void*) throw() { }
There are various uses for default placement. Bjarne Stroustrup originally observed, in his book The Design and Evolution of C++, that pointer placement new is necessary for hardware that expects a certain object at a specific hardware address. It is also required for the construction of objects that need to reside in a certain memory area, such as an area that is shared between several processors of a multiprocessor computer.[10] Other uses, however, include calling a constructor directly, something which the C++ language does not otherwise permit.[3] The C++ language does allow a program to call a destructor directly, and, since it is not possible to destroy the object using a p->~T();
Use casesPlacement new is used when you do not want operator new to allocate memory (you have pre-allocated it and you want to place the object there), but you do want the object to be constructed. Examples of typical situations where this may be required are:
The basic problem is that the constructor is a peculiar function; when it starts off, there is no object, only raw memory. And by the time it finishes, you have a fully initialized object. Therefore, i) The constructor cannot be called on an object ii) However, it needs to access (and initialize) non-static members. This makes calling the constructor directly an error. The solution is the placement form of operator new. This operator is implemented as: void* operator new(std::size_t count, void* here) { return here; }
void* operator new[](std::size_t count, void* here) { return here; }
Preventing exceptionsNormally, the (non-placement) new functions throw an exception, of type Programmers who wish to do this in their programs must include the Standard C++ library header #include <new>
struct T {};
int main() {
// Call the function operator new(std::size_t, const std::nothrow_t &) and (if successful) construct the object.
T* p = new (std::nothrow) T;
if (p) {
// The storage has been allocated and the constructor called.
delete p;
} else
; // An error has occurred. No storage has been allocated and no object constructed.
return 0;
}
Custom allocatorsPlacement syntax is also employed for custom allocators. This does not use any of the allocator and deallocator functions from the Standard C++ library header #include <cstdlib>
class A {
public:
void* allocate(std::size_t);
void deallocate(void*);
};
And define custom placement allocation and deallocation functions as follows:[7][8] void* operator new(std::size_t size, A& arena) {
return arena.allocate(size);
}
void operator delete(void* p, A& arena) {
arena.deallocate(p);
}
The program would employ the placement syntax to allocate objects using different instances of the A first_arena, second_arena;
T* p1 = new(first_arena) T;
T* p2 = new(second_arena) T;
Destroying an object whose storage is allocated in such a fashion requires some care. Because there is no placement delete expression, one cannot use it to invoke the custom deallocator. One must either write a destruction function that invokes the custom deallocator, or call the placement delete function directly, as a function call.[11][7][8] The former would resemble:[8] void destroy(T* p, A& arena) {
p->~T(); // First invoke the destructor explicitly.
arena.deallocate(p); // Then call the deallocator function directly.
}
which would be invoked from a program as: A arena;
T* p = new(arena) T;
/* ... */
destroy(p, arena);
The latter would involve simply writing the destructor invocation and delete function call into the program:[7][13] A arena;
T* p = new(arena) T;
/* ... */
p->~T(); // First invoke the destructor explicitly.
operator delete(p, arena); // Then call the deallocator function indirectly via operator delete(void*, A &).
A common error is to attempt to use a delete expression to delete the object. This results in the wrong DebuggingPlacement new can also be used as a simple debugging tool, to enable programs to print the filename and line number of the source code where a memory allocation has failed. This does not require the inclusion of the Standard C++ library header #if defined(DEBUG_NEW)
void* operator new(std::size_t size, const char* file, int line);
void* operator new[](std::size_t size, const char* file, int line);
void operator delete(void* p, const char* file, int line);
void operator delete[](void* p, const char* file, int line);
#define New new(__FILE__, __LINE__)
#else
#define New new
#endif
This would be employed in a program as follows:[9][14] T* p = New T;
The custom-written placement new functions would then handle using the supplied file and line number information in the event of an exception. For example:[9][14] #include <new>
#include <cstdlib>
class NewError {
public:
NewError(const char* file, int line) { /* ... */ }
/* ... */
} ;
void *
operator new(std::size_t size, const char* file, int line)
{
if (void* p = ::operator new(size, std::nothrow))
return p;
throw NewError(file, line);
}
Placement deleteAs noted above, there is no placement delete expression. It is not possible to call any placement The placement delete functions are called from placement The placement delete function that is called matches the placement new function that was invoked by the placement new expression. So, for example, if the following code is executed, the placement delete function that is called will be #include <cstdlib>
#include <iostream>
struct A {};
struct E {};
class T {
public:
T() { throw E(); }
};
void * operator new(std::size_t, const A&) {
std::cout << "Placement new called." << std::endl;
}
void operator delete(void*, const A&) {
std::cout << "Placement delete called." << std::endl;
}
int main(){
A a;
try {
T* p = new(a) T;
} catch (E exp) {
std::cout << "Exception caught." << std::endl;
}
return 0;
}
This is why the pointer placement delete functions are defined as no-operations by the Standard C++ library. Since the pointer placement new functions do not allocate any storage, there is no storage to [16] be deallocated in the event of the object's constructor throwing an exception.[11] If no matching placement delete function exists, no deallocation function is called in the event of an exception being thrown by a constructor within a placement SecurityPlacement new expressions are vulnerable to security exploits. In 2011, Kundu and Bertino[16] demonstrated some of the exploits on placement new. Some of the attacks are buffer overflow attacks, object overflow, selective stackguard overriding, virtual pointer subterfuge, memory misalignment attacks. In 2015, GCC released a patch[17] based on the findings in.[16] Notes
References
Further reading
|
Portal di Ensiklopedia Dunia