I want to override new operator in C++, by following the instructions at http://www.cprogramming.com/tutorial/operator_new.html
class CMyclass
{
public:
CMyClass(BOOL bUseWhichMemManager);
void* operator new(size_t);
void operator delete(void*);
};
I create two memory manager called CMemManager1 and CMemMangaer2, using different algorithms to allocate buffer. Now I want to control which memory manager to be used, when calling new.
I try to add a parameter bUseWhichMemManager to the constructor, but in overrided new function, there are no way to access the parameter. Is there a way to pass more parameters to new operator, such as:
void* operator new(size_t size, BOOL bUseWhichManager);
Thanks
You can pass parameters to an allocation function (i.e. operator new) in the new-expression like this:
new (b) CMyClass // passes "b" to "bUseWhichManager"
assuming you had the following declaration of operator new as a member of CMyClass, as in your question:
void* operator new(size_t size, BOOL bUseWhichManager);
(Don't put the parameter in the constructor, as it doesn't make sense, probably.)
All you need is a placement syntax to be defined for you custom allocation. Like:
void *
operator new (std::size_t size, CMemManager1& manager)
{
return manger.allocate(size) ;
}
void
operator delete (void * p, CMemManager1& manager)
{
manager.deallocate(p) ;
}
void *
operator new (std::size_t size, CMemManager2& manager)
{
return manger.allocate(size) ;
}
void
operator delete (void * p, CMemManager2& manager)
{
manager.deallocate(p) ;
}
and then
CMemManager1 manager1;
CMemManager2 manager2;
CMyclass * p1 = new (manager1) CMyclass ;
CMyclass * p2 = new (manager2) CMyclass ;
// It is a placement syntax so you should call your destructor manually.
p1->~CMyclass();
p2->~CMyclass();
operator delete(p1, manager1);
operator delete(p2, manager2);
Using the placement operator
#include<new>
#include<memory>
class A {
// Some stuff
};
A* a = (A*) _mm_malloc(sizeof(A),16);
// Now call the ctor without allocation
new a A(/* some arguments */);
// Delete
_mm_free(a);
Related
As a part of vector implementation I have to implement an allocator using functions malloc() and free() given the below interface:
class Allocator manages memory for class vector:
template<class T>
class Allocator {
public:
// function members
T* allocate(int n);
void deallocate(T* p, int n);
void construct(T* p, const T& v);
void destroy(T* p);
};
class Allocator member implementations:
/*
Function: allocate()
Use: allocator_object.allocator(num_of_elements)
Implicit in vector_base constructor.
It wraps malloc(); Throws bad_alloc
if allocation unsuccessful.
*/
template <class T>
T* Allocator<T>::allocate(int n) {
try {
std::auto_ptr<T> mem_ptr = reinterpret_cast<T*>(malloc(n * sizeof(T)));
} catch (std::bad_alloc& e) {
std::cerr <<"bad_alloc caught: "<< e.what() <<'\n';
throw;
}
return mem_ptr.release();
}
/*
Function: deallocate()
Use: allocator_object.deallocate(mem_ptr, redundant_par);
Implicit in base_vector destructor.
It returns memory to free store.
First argument is the pointer returned by malloc().
*/
template <class T>
void Allocator<T>::deallocate(T* mem_ptr, int n) {
free(mem_ptr);
}
/*
Function: construct()
Use: allocator_object.construct(elem_ptr[i], value)
Implicit in vector_base constructor
and all modifying members of vector.
It assigns the value passed as second
argument to the element with address
held by the pointer passed as a first
argument.
*/
template <class T>
void Allocator<T>::construct(T* p, const T& v = T()) {
*p = v;
}
/*
Function: destroy()
Use: allocator_object.destroy(elem_ptr[i]);
Implicitly in vector members: reserve(), resize();
It assigns type default value to the element
with address held by the pointer passed as argument.
*/
template <class T>
void Allocator<T>::destroy(T* p) {
*p = T(); // ? not sure if right
}
How to check for possible bad allocations from malloc() in function allocate()1, is the current method correct?
Edit:
Is the implementation of function destroy() correct2, could you give me an example of correct implementation?
There is an additional argument, n, in function deallocate() which I can't figure out how(what for) to use.
1. I know I'm using a deprecated std::auto_ptr, just following the book recommendations wrongly trying to get rid of the pointer in case of bad allocation.
2. I've read the documentation for function destroy(), but considering the fact that the allocated memory is one contiguous chunk that is free()d by passing the pointer returned by malloc(), the only reasonable object destruction is the assignment of a default value.
std::bad_alloc isn't magically generated. malloc fails by returning nullptr and there's nothing which would convert that into an exception. You call malloc, so you must check the return value manually.
destroy is wrong for the same reason as construct is wrong: They're a pair of functions that turn raw memory into an object and back. The normal way to do so is by placement new and direct invocation of the destructor. You're just calling the assignment operator there, which doesn't affect the objects lifetime at all.
I think this question might be a duplicate, but I don't know how to search for it.
I'm trying to overload operator new so that I can allow for a variable-length buffer after my class. Does my current design work as intended, or is it undefined behavior?
If the answer differs in C++98, C++03, and C++11 then please explain the differences.
struct POD { /* ...other POD members here... */ };
struct BufferedPOD : POD
{
size_t n;
BufferedPOD()
// Assume n is already initialized...
{
}
static void *operator new(size_t size)
{
return ::operator new(size);
}
static void *operator new(size_t size, size_t additional_size)
{
void *const p = operator new(size + additional_size);
static_cast<BufferedPOD *>(p)->n = additional_size;
return p;
}
static void operator delete(void *p)
{
return ::operator delete(p);
}
static void operator delete(void *p, size_t)
{
return operator delete(p);
}
};
int main()
{
std::auto_ptr<BufferedPOD> p(new (1000) BufferedPOD());
foo(p.get()); // do something with buffer
return 0;
}
First off, you are relying on undefined behavior, the memory is indeterminate upon calling the constructor.
In debug builds, it will often be filled with some marker-pattern for easier debugging, in release builds this freedom is generally just used to speed up the construction.
In both, reading indeterminate objects gets you UB, however that will play out in detail.
Anyway, you are going at it the wrong way (let's ignore violation of the "rule of three" for the time being:
Just declare matching overloads for ::operator new and ::operator delete, and a factory-function (which should be the only code with access to the only ctor you leave usable) which uses that and passes the extra-space on:
void* operator new(size_t a, size_t b) {
if(a+b< a || a+b < b)
throw new std::bad_alloc("::operator new(size_t, size_t) too big");
return operator new(a+b);
}
void operator delete(void* p, size_t a, size_t b) {return operator delete(p/*, a+b*/);}
struct Buffered : POD { // Not a pod due to inheritance
Buffered* make(size_t extra) {return new(extra) Buffered(extra);}
private:
size_t n;
Buffered(size_t extra) : n(extra) {}
Buffered(Buffered&) = delete;
void operator=(Buffered&) = delete;
};
I'm implementing for practice a smart pointer class.
I already defined an assignment operator overload that takes another instance of the same class. Now I want to define an overload of this operator, that takes any pointer. So I should be able to do stuff like smartPointer = &someObject; or smartPointer = NULL;, etc.
How can I go about doing that? Should I pass in a void*? Something else?
As a more general question (and I know this is rarely desired): what kind of parameter tells the compiler that any pointer can be passed in?
You can use a template function to make your object allow any pointer to be assigned to it.
template<typename T>
void operator=(T* obj)
{
//Your code here
}
However, its not a smart pointer if you could assign it any raw pointers as it could be assigned to more than one smart pointer object and then there would be a problem while deleting the pointer.
Following may help:
class MySharedPointer
{
public:
template <typename T>
MySharedPointer& operator = (T* p)
{
ptr.reset(p, [](void* p) { delete static_cast<T*>(p); });
return *this;
}
// nullptr is not a pointer, so it should have its own overload.
MySharedPointer& operator = (std::nullptr_t) {
ptr.reset();
return *this;
}
private:
std::shared_ptr<void> ptr;
};
Live example
I am using boost shared_ptr with my own memory manager like this (stripped down example, I hope there are no errors in it):
class MemoryManager
{
public:
/** Allocate some memory.*/
inline void* allocate(size_t nbytes)
{
return malloc(nbytes);
}
/** Remove memory agian.*/
inline void deallocate(void* p)
{
free(p);
}
};
MemoryManager globalMM;
// New operators
inline void* operator new(size_t nbytes, ogl2d::MemoryManagerImpl& mm)
{
return globalMM.allocate(nbytes);
}
// Corresponding delete operators
inline void operator delete(void *p, ogl2d::MemoryManagerImpl& mm)
{
globalMM.deallocate(p);
}
/** Class for smart pointers, to ensure
* correct deletion by the memory manger.*/
class Deleter
{
public:
void operator()(void *p) {
globalMM.deallocate(p);
}
};
And I am using it like this:
shared_ptr<Object>(new(globalMM) Object, Deleter);
But now I am realizing. If the shared_ptr deletes my onject, it calls Deleter::operator() and the objects gets deleted. But the destructor does not get called ...
How can I change this?
Because deleter should destroy the object:
class Deleter
{
public:
void operator()(Object *p) {
p->~Object();
globalMM.deallocate(p);
}
};
Edit: I was wrong in my deleter, fixed
You can explicitly call the destructor (which means that the Deleter should probably receive a T * instead of a void *). Note that the code you provided doesn't actually use placement new/delete, so my answer is only meaningful for this particular example.
I have implemented a custom allocator (to be used by STL containers within my memory debugging utility, without them using my overridden new operator). Within the memory debugger I use an instance of the same allocator class to allocate the objects I need to keep track of 'normal' memory allocations. It's all working fine, but I'm not sure if the way I'm using the allocator interface is correct. Here are the utility methods as they currently stand (correct initialization parameters for the entry will be added soon):
iidebug::CMemoryDebugger::CEntry* iidebug::CMemoryDebugger::NewEntry()
{
CEntry* pEntry = m_entryAllocator.allocate(1);
if (0 != pEntry)
{
new(pEntry) CEntry(0, 0, __FILE__, 0, 0, 0);
}
return pEntry;
}
void iidebug::CMemoryDebugger::DeleteEntry( iidebug::CMemoryDebugger::CEntry* pEntry )
{
if (0 != pEntry)
{
destruct(pEntry);
m_entryAllocator.deallocate(pEntry, 1);
}
}
This just seems very messy, but I can't see how I can improve it.
You can actually overload new and delete to take an allocator parameter, like so:
inline void *operator new(size_t sizeT, Allocator *&a) {
return a->allocate(sizeT);
}
inline void operator delete(void * mem, Allocator *&a) {
a->release(mem);
}
int main()
{
Allocator * a = new Allocator;
C *c = new(a) C;
c->~C();
operator delete(c, a);
return 0;
}
See the wikipedia article for more detail. It's still a bit messy because you have to be sure not to call the regular delete operator if your allocator does something special.
Just for reference in case anyone struggles to use Drew's code, it needed a few tweaks. The following is what I ended up using:
template <typename T>
void* operator new(SizeT iSize, SimpleAllocator<T>& rAllocator)
{
return rAllocator.allocate(1);
}
template <typename T>
void operator delete(void* pvMemory, SimpleAllocator<T>& rAllocator)
{
((T*)pvMemory)->~T();
rAllocator.deallocate(pvMemory, 1);
}
And actually using it is as simple as:
// SimpleAllocator<CFoo> fooAllocator = ...
CFoo* pFoo = new(fooAllocator) CFoo(param1, param2, ...);
// Do stuff with pFoo...
operator delete(pFoo, fooAllocator);
What is destruct? I suppose it should be:
void iidebug::CMemoryDebugger::DeleteEntry( iidebug::CMemoryDebugger::CEntry* pEntry )
{
if (0 != pEntry)
{
pEntry->~CEntry();
m_entryAllocator.deallocate(pEntry, 1);
}
}