|
-
August 14th, 2010, 12:44 PM
#1
Replacing global new and delete
Hi everyone! I've been reading many an article on this matter, but I want to make sure I've understood everything correctly. The thing is, that I couldn't find any source that explicitly "validated" what I plan to do in my specific case, so I though I'd ask more experienced people than me.
I want to replace global operators new and delete. There are two reasons I want to do this, and to each of them corresponds a "step" I want to be sure of:
1) I want to do some simple tracking of my allocations; keeping a record of how much memory I allocated in the heap, and who allocated it, should help me in spotting memory leaks;
2) I may want to do in the future some sort of custom memory allocation. While I know this is usually a form of optimization, and it's wrong to optimize early in the project, I would like to create something which will allow me to drop in a custom allocator without too much hassle.
From what I've got, point 1 is very easy to achieve. Basically I only need to define an overload of the global new with additional parameters, like:
Code:
void* operator new(size_t size, const char* file, const char* function /*, more parameters as needed */)
Of course, I'll have to define a symmetric delete operator that takes the same arguments, to avoid leaks should the constructor throw an exception. Question #1: if I don't use exceptions in my code, do I still have to define this delete overload? Do I need to worry about the nothrow overloads, or is it safe to ignore them?
At first I wondered what would happen if I also replaced the default delete definition (the delete(void* ptr) one). Let's suppose my new/delete overloads use malloc()/free(); the objects I create will be created with malloc() and deleted with free(), but the objects that third-party code would create (with the "regular" new operator) would be deleted with free(), but I could not be sure whether they were created with malloc(). This, of course, would be bad.
Then I figured that my overloads of new and delete could, in fact, simply call the regular new and delete after they did their record-keeping stuff. This way, all of the objects (both mine and third-parties') would be created with the allocator used by the C++ implementation, and deleted with the very same allocator. I've read articles that confirm this idea; is this correct (question #2)? I have to ask because it's hard for me to write a test for this kind of problems; maybe I'm just being lucky and using by chance the same allocator my compiler uses; maybe I just didn't make a test valid enough to observe bad behaviour when I do something wrong.
The second reason why I need to fiddle with new and delete seems to be way trickier. From what I've got, to implement a custom allocator I would be better off splitting the two operations performed by new. So, in my "step 2" overload of new there would be
a) the record-keeping stuff
b) allocation of memory by the allocator - something like
Code:
void* pObject = MemoryAllocator::Allocate(size);
c) construction of the object via non-overloaded placement new
Symmetrically, my "step 2" delete would feature
a) the record-keeping stuff
b) destruction of the object with an explicit call of the destructor method
c) deallocation of the memory pointed to: MemoryAllocator::Free(ptr)
Question #3: is it correct that I have to use operator new placement with a custom allocator? As far as I know, I can't pass an allocator object to new. What I am confused about, is: how would I know the class of the object to construct? The same applies for the destructor.
Question #4: would it be a better choice to leave the new/delete operators as they were in step 1 (i.e., with the call to the regular operators), and to proceed to override them on a class basis, so that I can define a base class for all the objects that will need a particular allocator (say, class PooledObject, which all objects that need to be pooled will derive from), while leaving the rest of the allocations untouched?
Also, marginally, question #5: should the record-keeping code go before or after the construction/destruction of the object? Of course it's wise to not separate allocation from construction (and destruction from deallocation). I guess it would be better to do the construction first, and then the record-keeping, so that the construction is not recorded if something goes wrong (exceptions); symmetrically, destroy the record (if it exists) then destroy the object. Am I correct?
Thanks a lot for the patience to read all of this! :-)
I owe Paul McKenzie a pizza.
I am no expert; but they say I can make concepts easy to understand. That's why newbies questions are mine!!! XD
-
August 16th, 2010, 12:33 AM
#2
Re: Replacing global new and delete
Last edited by nuzzle; August 16th, 2010 at 10:46 PM.
-
August 16th, 2010, 12:40 PM
#3
Re: Replacing global new and delete
Thanks Nuzzle. Your reply was not really spot-on, but it gave me an interesting thing to observe: operator new only takes care of the memory allocation, and not of the construction of the object.
Here's the code of my current operator new - the one I labelled "step 1" in my opening post:
Code:
void* operator new(size_t size, const char* file, size_t line, const char* function)
{
void* ptr = ::operator new(size);
Utilities::MemoryManager::GetInstance().RecordAllocation(ptr, size, file, line, function);
return ptr;
}
With breakpoints on operator new(size) and on the first line of the constructor, I was able to see that the operator new (the one you see above) is called first, completely executed (i.e., regular new, record-keeping, and return), *then* the constructor is executed.
So, I don't need to worry about constructing and destructing the objects. It is enough (and Nuzzle's example shows it for good) to take care of memory allocation and deallocation; the compiler will automagically call the ctor/dtor where appropriate.
I checked this behaviour in MS Visual Studio C++ Express 2009; does anybody know if this is part of the standard, or whether this is supported by a large number of compilers? I'm mainly interested in MS's ones and in GCC.
I owe Paul McKenzie a pizza.
I am no expert; but they say I can make concepts easy to understand. That's why newbies questions are mine!!! XD
-
August 16th, 2010, 10:45 PM
#4
Re: Replacing global new and delete
 Originally Posted by Sk#
Your reply was not really spot-on
You can't appreciate the considerable value of a practical working example?
Well, don't worry, I'll remove it.
Last edited by nuzzle; August 16th, 2010 at 10:50 PM.
-
August 17th, 2010, 02:40 AM
#5
Re: Replacing global new and delete
You should try to understand more in details how new works, in particular the difference between the new operator and operator::new, and what their respective jobs are.
the keyword new, also know as the "new operator" first calls "operator::new", providing it with a certain amount of arguments. operator::new's job is to allocate* as much memory as requested by the new operator. How it does it depends on the arguments passed.
Once operator::new is done, it returns a pointer to the new operator, and the new operator is then responsible for constructing the object (objects in case of new[]) at the place returned by operator::new.
operator::new is over-loadable. the new operator is a non-overloadable keyword.
*operator::new's job is not actually allocating memory, but rather giving the new operator a place to construct its object.
---
Now for my 2 cents: All of this is pointless. I have NEVER (EVER) seen anybody actually overload operator new. ever. I can understand doing it for the fun of learning (I have).
What I could recommend though is to make a custom allocator. These are more powerful and polyvalent objects that can suit your future needs, without changing the global behavior of new.
Also, I know for a fact the GCC provides a lot of safe/checked allocators. Try to read about those before steamrolling out your own application.
Is your question related to IO?
Read this C++ FAQ article at parashift by Marshall Cline. In particular points 1-6.
It will explain how to correctly deal with IO, how to validate input, and why you shouldn't count on "while(!in.eof())". And it always makes for excellent reading.
-
August 17th, 2010, 04:19 AM
#6
Re: Replacing global new and delete
 Originally Posted by nuzzle
You can't appreciate the considerable value of a practical working example?
Well, don't worry, I'll remove it.
No, don't get me wrong: your example was gold and it was very helpful. Sorry if that didn't come out. What I meant was that you didn't really answer my questions, though the example gave me useful hints.
Please, restore the example - if not for me, for those who will search this topic in the future.
 Originally Posted by monarch_dodra
You should try to understand more in details how new works, in particular the difference between the new operator and operator::new, and what their respective jobs are.
[...]
I swear, I had never heard about such a difference! I'll be sure to research more now that you pointed this out, because this explains what I had observed in my previous post.
 Originally Posted by monarch_dodra
Now for my 2 cents: All of this is pointless. I have NEVER (EVER) seen anybody actually overload operator new. ever. I can understand doing it for the fun of learning (I have).
What I could recommend though is to make a custom allocator. These are more powerful and polyvalent objects that can suit your future needs, without changing the global behavior of new.
Also, I know for a fact the GCC provides a lot of safe/checked allocators. Try to read about those before steamrolling out your own application.
Thanks, I'll look them up. It wasn't clear from my post, but I don't plan to write an allocator myself from scratch; though I will probably have to use a "custom" one - custom in the sense that it's not the default one.
If I'm not wrong, though - and Nuzzle's example proved it, - when I use a custom allocator I have to drop it in my project, and its "entry point" would be operator new, am I wrong? So I need to override the default operator, to make it allocate memory (pardon, find a place to construct the object) using the custom allocator.
Also, would the allocator keep track of allocations? Well, probably yes, but I'm interested in those extra information, and apparently I'm doing no harm by just wrapping operator new in a record-keeping function. Am I correct? There may be bad things I'm doing that I don't see.
I owe Paul McKenzie a pizza.
I am no expert; but they say I can make concepts easy to understand. That's why newbies questions are mine!!! XD
-
August 17th, 2010, 04:37 AM
#7
Re: Replacing global new and delete
an allocator (in the stl sense of the word) is basically an instantiable new-functor. The problem with new is that it is global. If you overload-it, you overload it for the entire project. With allocators, you can have several different allocators co-existing, each providing their own allocating scheme. If you have ever wondered what the second parameter of a std::vector, that's what it is.
http://www.cplusplus.com/reference/s...ory/allocator/ is the default stl allocator, and basically wraps new. You can create your own, and provided you keep the same interface as the stl's, you can have a vector using your own allocating scheme.
 Originally Posted by Sk#
Also, would the allocator keep track of allocations?
They can do whatever you want. I believe that in debug in GCC, the default allocator does this by default.
Last edited by monarch_dodra; August 17th, 2010 at 04:39 AM.
Is your question related to IO?
Read this C++ FAQ article at parashift by Marshall Cline. In particular points 1-6.
It will explain how to correctly deal with IO, how to validate input, and why you shouldn't count on "while(!in.eof())". And it always makes for excellent reading.
-
August 17th, 2010, 08:11 AM
#8
Re: Replacing global new and delete
 Originally Posted by monarch_dodra
I'm no expert, but I would guess that it's actually wrapping malloc, since I know std::vector uses placement new internally to avoid constructing objects in the "unused" portion of its capacity until necessary.
-
August 17th, 2010, 08:36 AM
#9
Re: Replacing global new and delete
 Originally Posted by Lindley
I'm no expert, but I would guess that it's actually wrapping malloc, since I know std::vector uses placement new internally to avoid constructing objects in the "unused" portion of its capacity until necessary.
Allow me to correct myself: it calls operator::new. This allocates memory, but does not construct an object on the allocated spot.
GCC implements std::allocate by deriving from the base class ext/new_allocator, which does this:
Code:
pointer
allocate(size_type __n, const void* = 0)
{
if (__builtin_expect(__n > this->max_size(), false))
std::__throw_bad_alloc();
return static_cast<_Tp*>(::operator new(__n * sizeof(_Tp)));
}
// __p is not permitted to be a null pointer.
void
deallocate(pointer __p, size_type)
{ ::operator delete(__p); }
Is your question related to IO?
Read this C++ FAQ article at parashift by Marshall Cline. In particular points 1-6.
It will explain how to correctly deal with IO, how to validate input, and why you shouldn't count on "while(!in.eof())". And it always makes for excellent reading.
-
August 17th, 2010, 02:25 PM
#10
Re: Replacing global new and delete
 Originally Posted by monarch_dodra
Now for my 2 cents: All of this is pointless. I have NEVER (EVER) seen anybody actually overload operator new. ever. I can understand doing it for the fun of learning (I have).
I realize this is your $0.02, but I have overloaded operator new and delete for a parser token class. The class would keep a cache of tokens and when the parser "allocated" a new one, the class would just pass it an already used one.
Not only did parsing large files speed up, it used less memory.
Viggy
-
August 17th, 2010, 02:28 PM
#11
Re: Replacing global new and delete
The matrix library Eigen overloads new and delete to ensure its matrices have a particular memory alignment, so that it can automatically generate SSE-accelerated code.
-
August 17th, 2010, 03:21 PM
#12
Re: Replacing global new and delete
Hehe, not saying it can't be done, I just said I had never personally seen it.
Is your question related to IO?
Read this C++ FAQ article at parashift by Marshall Cline. In particular points 1-6.
It will explain how to correctly deal with IO, how to validate input, and why you shouldn't count on "while(!in.eof())". And it always makes for excellent reading.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|