1

I'm in the process of porting my game engine from Java to C++. I've been doing a lot of research on custom memory allocators as I now need to worry about memory management. Every article that I've read on the topic says that you should overload the new and delete operators. However, these overloaded operators usually just delegate to and call a custom function that handles the memory allocation. Furthermore, macros are commonly used such as:

#define New(className, size, description) customNew(className, size, description, __FILE__, __LINE__)

which never even use the overloaded new operator, and simply calls the custom method. I understand how the macros are useful as you can get the filename and line numbers nicely, but why overload new and delete if in code you're simply going to use the macro New and not the operators.

Also, overriding (not overloading) the default new seems somewhat pointless when our custom allocator may want to know additional information such as a description, a filename and line numbers. We can't possibly know that information on a standard invocation of newand can't delegate it to our customNew in a meaningful way.

I'm failing to see what the great benefit is of overloading and overriding these operators.

It seems to me that calling:

customNew(className, size, desc, file, line);

is more intuitive than calling the overloaded new:

new (size, desc, file, line) className();

Finally, if I were to override and overload, where is the best place to do so? Doing it per class is obnoxiously tedious. Can it be done by namespace? I plan on having separate projects for each sub-system so that they can be maintained independently. How would I facilitate overriding and overloading across the projects and then within the game's project? Pardon my C++ n00bishness. It's been a while.

Wagan8r
  • 468
  • 5
  • 18
  • What is your purpose in wanting to provide these alternatives in the first place? – Nicol Bolas Jul 18 '12 at 20:29
  • this is kind of a dup of http://stackoverflow.com/questions/7149461/why-would-one-replace-default-new-and-delete-operators – nate_weldon Jul 18 '12 at 20:33
  • 1
    If you want a separate memory allocator for each and every class, you *can* overload these. If you don't want to, just don't. ;-) – Bo Persson Jul 18 '12 at 20:47
  • No, I don't want separate allocators for each class. I'd prefer to route every allocation through a single method/operator. My main question is WHERE should these methods/operators be defined, and WHAT is the difference between simply writing a custom method to allocate memory vs. overriding/loading `new` to use my custom method? – Wagan8r Jul 18 '12 at 21:22

2 Answers2

3

I'm failing to see what the great benefit is of overloading and overriding these operators.

There's the obvious point: you can't escape it (not without work).

If you want all your memory allocation to go through one location, all it takes for someone to screw that up is this:

new Foo();

However, if you globally override operators new and delete, they have to do more work:

void * ptr = malloc(sizeof(Foo));
new(ptr) Foo;

There is also the point that the vast majority of C++ programmers are used to new and delete, which they will use by default. If you require them to use a function, that becomes something you have to teach them and they have to learn. And if they make a mistake and use the standard way, see above.

Oh, and then there's the syntax:

customNew(className, size, desc, file, line);

That's not going to work. That may allocate memory, but it will not construct a type. It is no different from malloc. So the very name is a lie; it's not "new"-ing anything. It's just allocating.

You would need to use C++11 variadic templates and in-place construction to do things correctly:

template<typename T, typename ...Args>
T *customNew<T>(const char *className, const char *desc, size_t file, size_t line, Args&&... args)
{
  void* ptr = allocate_memory(className, sizeof(T), desc, file, line);
  return new(ptr) T(std::forward<Args>(args)...);
}

Personally, I'd say that the new(...) T(...) syntax is much cleaner than the customNew<T>(...) version. It's clear which parameters go to the allocator, and which parameters go to the type itself. It's certainly more standard.

But WHERE do I override the global new and delete

Globally. As in the global namespace. You stick the overloads in a header somewhere and include it everywhere.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • Awesome, thanks for the help. However, I still have one question about the usage of `new`. Suppose that I want to always use the overloaded version of `new` that has the extra information such as a description, filename, and line number parameters. Is there anything that I can do to enforce the usage of `new(size_t size, params...)`, or do I just need to record the "improper" usage of `new(size_t size)` and retroactively fix these bad allocations? Thanks. – Wagan8r Jul 19 '12 at 02:55
  • 1
    @Wagan8r: You could make `new(size_t size)` assert or throw an exception. – Nicol Bolas Jul 19 '12 at 03:19
  • @Nik-Lz: The example was of what you would have to do to avoid someone who globally override `new` and `delete`. So calling `::operator new` like that won't avoid that override; calling `malloc` will. – Nicol Bolas Oct 28 '18 at 14:36
2

You might have a general purpose allocator that is garbage collected, aligned, and has a maximum size limit, which might be possible constraints of your spec. If you use external code that makes use of new/delete, you could redirect requests to this allocator as some kind of low-level/system memory space.

Also, articles I read often go after the global operators as a start. Here is a good stackoverflow post on the subject.

Community
  • 1
  • 1
Derek E
  • 742
  • 1
  • 6
  • 13
  • Yes, I've read that article multiple times, as well as this 4-part blog post http://jfdube.wordpress.com/2011/10/03/memory-management-part-1-introduction/. But WHERE do I override the global `new` and `delete` in addition to any overloads?? – Wagan8r Jul 18 '12 at 21:17