|
Table of Contents
|
Motivation
At present we can override operator new as follows:
void* operator new(size_t size, ArgT1 arg1, ArgT2 arg2, ...) { return malloc(size); // just an example }
Even though it is good enough, still I think something is missing here. For example, if I want to implement a debug version of operator new which reports the memory leak and the kind of objects leaked, I cannot get the class name of an allocated object.
An operator new with typeinfo can solve this problem. And in fact, it has more potential applications.
Impact on the Standard
It is only a pure extension of operator new. It doesn't change semantic interpretation of existed standards except calling new Type(a1, a2, …) and new Type[count].
Design Decisions
operator new with typeinfo
We introduce a new operator named "operator _new". Please refer to the following expressions:
template <typename Type> void* operator _new(ArgT1 arg1, ArgT2 arg2, ...) { return malloc(sizeof(Type)); // just an example }
When we create an object by operator new:
Type* obj = new(arg1, arg2, ...) Type(a1, a2, ...);
It means:
if an operator _new<Type> is matched:
if a normal operator new is also matched:
report error: ambiguous call
else:
void* ptr = operator _new<Type>(arg1, arg2, ...);
obj = new(ptr) Type(a1, a2, ...);
else:
Use formerly standard process;
// void* ptr = operator new(sizeof(Type), arg1, arg2, ...);
// obj = new(ptr) Type(a1, a2, ...);
operator new[] with typeinfo
We introduce a new operator named "operator _new[]". Please refer to the following expressions:
template <typename Type> void* operator _new[](size_t size, size_t count, ArgT1 arg1, ArgT2, ...) { assert(size >= sizeof(Type) * count); // depends return malloc(size); // just an example }
When we create an array by operator new[]:
Type* obj = new(arg1, arg2, ...) Type[count];
It means:
if an operator _new[]<Type> is matched:
if a normal operator new is also matched:
report error: ambiguous call
else:
size_t size = sizeof(Type) * count + extra_bytes_store_array_size;
void* ptr = operator _new[]<Type>(size, count, arg1, arg2, ...);
void* array = store_array_size_and_return_array_buffer(ptr, count);
obj = construct<Type>(array, count);
else:
Use formerly standard process;
Issues for Discussion
I. Corresponding operator _delete
Do we need a corresponding operator _delete/_delete[]? My answer is NO. If a standard form of delete pointer is not suitbale, then we can think about defining a corresponding destroy function.
Applications based on operator new with typeinfo
I. A Debug Version of operator new
struct DbgAllocInfo { const std::type_info* typeinfo; const char* file; int line; size_t count; }; std::map<void*, DbgAllocInfo> g_mapDbgInfo; template <typename Type> void* operator _new(const char* file, int line) { void* ptr = malloc(sizeof(Type)); DbgAllocInfo info = { &typeid(Type), file, line, 1 }; g_mapDbgInfo.insert(std::pair<void*, DbgAllocInfo>(ptr, info)); return ptr; } template <typename Type> void* operator _new[](size_t size, size_t count, const char* file, int line) { assert(size >= sizeof(Type) * count); // depends void* ptr = malloc(size); DbgAllocInfo info = { &typeid(Type), file, line, count }; g_mapDbgInfo.insert(std::pair<void*, DbgAllocInfo>(ptr, info)); return ptr; } void operator delete(void* p) { size_t ret = g_mapDbgInfo.erase(p); assert(ret == 1); free(p); } void operator delete[](void* p) { size_t ret = g_mapDbgInfo.erase(p); assert(ret == 1); free(p); } void report_memory_leaks() { for (const std::pair<void*, DbgAllocInfo>& leak : g_mapDbgInfo) // for each { report_leak(leak.first, leak.second); } }
II. A Region Allocator
typedef void (*DestructorType)(void* data); class RegionAlloctor { // Allocate memory without given a cleanup function void* allocate(size_t cb); // Allocate memory with a cleanup function void* allocate(size_t cb, DestructorType fn); // Cleanup and deallocate all allocated memory ~RegionAlloctor(); }; template <class Type> struct DestructorTraits { static void destruct(void* data) { ((Type*)data)->~Type(); } static void destructArray(void* data) { void* array = std::get_array_buffer(data); // depends size_t count = std::get_array_size(data); // depends destruct<Type>(array, count); } }; template <typename Type> void* operator _new(RegionAlloctor& alloc) { return alloc.allocate(sizeof(Type), DestructorTraits<Type>::destruct); } template <typename Type> void* operator _new[](size_t size, size_t count, RegionAlloctor& alloc) { assert(size >= sizeof(Type) * count); // depends return alloc.allocate(size, DestructorTraits<Type>::destructArray); } void main() { RegionAlloctor alloc; int* intObj = new(alloc) int; int* intObjWithArg = new(alloc) int(10); int* intArray = new(alloc) int[count]; MyObj* obj = new(alloc) MyObj; MyObj* objWithArg = new(alloc) MyObj(100); MyObj* objArray = new(alloc) MyObj[count]; // Don't need to call operator delete }
Proposed Text
template <typename Type> void* operator _new(ArgT1 arg1, ArgT2 arg2, ...); template <typename Type> void* operator _new[](size_t size, size_t count, ArgT1 arg1, ArgT2, ...); namespace std { void* get_array_buffer(void* data); size_t get_array_size(void* data); }





