Suppose, we have hierarchy of classes and we want to make them allocate/deallocate their memory only throughout our memory manager. What is a classical C++ way to achieve this behavior?
Is it a MUST to have additional checks such as:
class Foo{
public:
virtual ~Foo(){}
void* operator new(size_t bytes)
{
if (bytes != sizeof(Foo)){
return ::operator new(bytes);
}
return g_memory_manager.alloc(bytes);
}
void operator delete(void *space, size_t bytes)
{
if (bytes != sizeof(Foo)){
return ::operator delete(space);
}
g_memory_manager.dealloc(space, bytes);
}
}
No the checks are unnecessary.
Have a look at Alexandrescu's Loki's SmallObject allocator, you just inherit from SmallObject and it does all the heavy lifting for you!
And do not forget to overload all versions of new and delete:
simple version
array version
placement version
Otherwise you might have some troubles.
my comments:
make creators to do the things, for example:
template<class t>
struct creator_new { // a creator using new()/delete()
static t *Create(void)
{
return new t;
}
static void Destroy(t *p)
{
delete p;
}
};
you could extend the creator to check memory leak, or use memory pool to manage your objects etc.
Related
I was trying to understand the c++17 pmr.
So I did this and it is not working as I thought, what could go wrong?
template <typename T>
class Memory : public std::experimental::pmr::memory_resource {
public:
Memory() { this->memory = allocate(sizeof(T), alignof(T)); }
void *getMemory() { return this->memory; }
~Memory() { deallocate(this->memory, sizeof(T), alignof(T)); }
private:
void *do_allocate(std::size_t bytes, std::size_t alignment)
{
memory = ::operator new(bytes);
}
void do_deallocate(void *p, std::size_t bytes, std::size_t alignment)
{
::operator delete(memory);
}
bool do_is_equal(
const std::experimental::pmr::memory_resource& other) const noexcept
{
}
void *memory;
};
what can be going wrong with my implementation?
This is the client..
Memory<std::string> mem;
std::string * st = (std::string*)mem.getMemory();
st->assign("Pius");
std::cout << *st;
The polymorphic resource allocators allocate memory; that's all they do. Unlike container Allocators, they don't create objects. That's why they return void*s.
Memory resources are not meant to be used by themselves. That's why std::polymorphic_allocator<T> exists. You can also do the object creation/destruction yourself, using placement-new and manual destructor calls.
Also, your memory_resource implementation makes no sense. do_allocate should return the allocated memory, not store it internally. Your function provokes undefined behavior by returning nothing (which your compiler should have warned about).
I just came across some container implementation in C++. That class uses an internal buffer to manage its objects. This is a simplified version without safety checks:
template <typename E> class Container
{
public:
Container() : buffer(new E[100]), size(0) {}
~Container() { delete [] buffer; }
void Add() { buffer[size] = E(); size++; }
void Remove() { size--; buffer[size].~E(); }
private:
E* buffer;
int size;
};
AFAIK this will construct/destruct E objects redundantly in Container() and ~Container() if new/delete are not customized. This seems dangerous.
Is using placement new in Add() the best way to prevent dangerous redundant constructor / destructor calls (apart from binding the class to a fully featured pool)?
When using placement new, would new char[sizeof(E)*100] be the correct way for allocating the buffer?
AFAIK this will construct/destruct E objects redundantly
It would appear so. The newed array already applies the default constructor and the delete[] would call destructor as well for all the elements. In effect, the Add() and Remove() methods add little other than maintain the size counter.
When using placement new, would new char[sizeof(E)*100] be the correct way for allocating the buffer?
The best would be to opt for the std::allocator that handles all of the memory issues for you already.
Using a placement new and managing the memory yourself requires you to be aware of a number of issues (including);
Alignment
Allocated and used size
Destruction
Construction issues such as emplacement
Possible aliasing
None of these are impossible to surmount, it has just already been done in the standard library. If you are interested in pursuing a custom allocator, the global allocation functions (void* operator new (std::size_t count);) would be the appropriate starting point for the memory allocations.
Without further explanation on the original purpose of the code - a std::vector or a std::array would be far better options for managing the elements in the container.
There's a number of issues with the code.
If you call Remove() before calling Add() you will perform assignment to a destructed object.
Otherwise the delete[] buffer will call the destructor of 100 objects in the array. Which may have been called before.
Here's a valid program:
#include <iostream>
int counter=0;
class Example {
public:
Example():ID(++counter){
std::cout<<"constructing "<<ID<<std::endl;
}
~Example(){
std::cout<<"destructing "<<ID<<std::endl;
ID=-1;
}
private:
int ID;
};
template <typename E> class Container
{
public:
Container() : buffer(new char [100*sizeof(E)]), size(0) {}
~Container() {
for(size_t i=0;i<size;++i){
reinterpret_cast<E*>(buffer)[i].~E();
}
delete [] buffer;
}
void Add() { new (buffer+sizeof(E)*size) E(); size++; }
void Remove() { reinterpret_cast<E*>(buffer)[--size].~E(); }
private:
void* buffer;
size_t size;
};
int main() {
Container<Example> empty;
Container<Example> single;
Container<Example> more;
single.Add();
more.Add();
more.Remove();
more.Add();
more.Add();
more.Remove();
return 0;
}
Let's say that you're on a system where you cant use TR1, boost, etc. You just have the straight-up standard library C++.
If you were stuck with this scenario, what's the simplest reference-counting smart-pointer you could use? (our system only has auto_ptr which is not overly useful).
I'm happy with a link to something somewhat well-established or a simple "it's too complicated to implement yourself" if that's the answer (which I'm half expecting :( ).
I would probably go with std::shared_ptr, which was approved in C++0x two weeks ago (so no TR1 needed). In all seriousness, I would recommend boost (or upgrading). You certainly can implement it yourself, but the cost-benefit doesn't make sense, since constructs like this are complicated to do right.
Well some time ago I just made one for studding interest. It's not so "smart" but anyway better then raw pointer.
class CReferenceCount
{
private:
unsigned int m_count;
public:
CReferenceCount() : m_count(0) { }
virtual ~CReferenceCount() { }
void increseRef()
{
++m_count;
}
unsigned int decreseRef()
{
return --m_count;
}
};
class CCustomDeleter
{
public:
template<typename T>
void operator()(const T* ptr) const
{
delete ptr; ptr = NULL;
}
void operator()(const char* ptr) const
{
delete[] ptr; ptr = NULL;
}
};
template <typename T>
class CWrapPtr
{
private:
void makeRefCountObj()
{
try
{
m_rcPtr = new CReferenceCount();
}
catch (std::bad_alloc &err)
{
std::cout<<"-- CWrapPtr : "<<err.what()<<std::endl;
// should do something about failed CWrap object...
}
m_rcPtr->increseRef();
}
public:
T *m_objPtr;
CReferenceCount *m_rcPtr;
CWrapPtr() : m_objPtr(NULL), m_rcPtr(NULL)
{
makeRefCountObj();
}
CWrapPtr(T *obj) : m_objPtr(obj), m_rcPtr(NULL)
{
makeRefCountObj();
}
virtual ~CWrapPtr()
{
if (m_rcPtr && m_rcPtr->decreseRef() == 0)
{
CCustomDeleter dd;
dd(m_objPtr);
delete m_rcPtr; m_rcPtr = NULL;
}
}
CWrapPtr(const CWrapPtr<T> &other) : m_objPtr(other.m_objPtr),
m_rcPtr(other.m_rcPtr)
{
m_rcPtr->increseRef();
}
T& operator*()
{
assert(m_objPtr != NULL);
return *m_objPtr;
}
T* operator->()
{
assert(m_objPtr != NULL);
return m_objPtr;
}
CWrapPtr<T>& operator=(const CWrapPtr<T> &other)
{
if (this != &other)
{
if (m_rcPtr && m_rcPtr->decreseRef() == 0)
{
CCustomDeleter dd;
dd(m_objPtr);
delete m_rcPtr; m_rcPtr = NULL;
}
m_objPtr = other.m_objPtr;
m_rcPtr = other.m_rcPtr;
m_rcPtr->increseRef();
}
return *this;
}
};
And yeah it's just demo..
If you are lucky, then you havent designed the classes that need shared pointer but dont have a readymade one.
If you are lucky and your entire program will run in single thread ...
Then you have chance of having a really cheap shared pointer.
Use a base class SharedPtr. Derive all the object from that.
Objects of type SharedPtr will contain some free-store memory for counting.
When you are copying it, do increment
When you are destroying it decrement and free the object if necessary.
Et cetra et cetra of the shared pointer semantics.
Store pointers as SharedPtr. and do downcast whenever you need some operation.
I know this is cheapo solution, but...
1. Multithreaded will require locks. Costly + involved.
2. Template programming. Will probably require a half-day and another half day to debug and fix your issues, which you will get when you try to replicate shared_ptr from BOOST
If yo uhave just couple of classes whose existence is more important and not with hundreds of operations on them, then you can try this approach.
Btw, this is kind of "template-pattern"
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);
}
}