I've overloaded the global operator new/delete/new[]/delete[] but simple tests show that while my versions of new and delete are being called correctly, doing simple array allocations and deletes with new[] and delete[] causes the implementations in newaop.cpp and delete2.cpp to be called.
For example, this code
int* a = new int[10];
calls operator new[] in newaop.cpp, which in turn calls my version of operator new. So it seems they are globally overloaded but for some reason not the array versions. Is there something I'm missing?
EDIT: My implementation of the operators are in a separate project which is compiled into a library and linked to statically. In retrospect, this might have been useful to include in the original post, as it probably has something to do with this. Although I still can't figure out why only the array versions are affected.
I don't know how you overloaded operator new[] but I just tried it with MSVC2008:
void* operator new[](size_t size)
{
return 0;
}
int main()
{
int* a = new int[5];
}
The code above effectively calls my faulty implementation of operator new[].
So here is my guess: you failed at overloading operator new[] for some reason and your program uses the compiler's version of operator new[] which relies on operator new to allocate the memory. Since you overloaded operator new, your implementation gets called.
operator new allocates one object and calls its constructor. new[] allocates n objects and calls n constructors.
edit: Having multiple new[] and delete[] overloads could be considered a bit of and odd one. How does the compiler know which one to link too? Any chance you could post your overloads? Also do yours get called if you don't link newaop and delete2 in (ie yours are the only implementations in the exe)?
You told us your code is buggy but didn't post any of it :vP . My guess would be that you used int as the argument of the overload rather than size_t. Some overloads might get called because the compiler decided to be tolerant in that instance.
Ok, I managed to crack this so am posting in case anyone else stumbles upon this.
The reason for the operator not being called was because my implementation was located in a library, not in the project which called the operators. In fact, since technically you only need to include an implementation of the operators, they are already globally defined, I only specified the implementation of the operators in a .cpp in my library (this was the wrong step). The code obviously only included the header files from the library and didn't ahve visibility to the implementations. Moreover, Visual Studio seems to have linked newaop.cpp and delete2.cpp into my application. These two files contain implementations for operator new[] and operator delete[] (bot not for regular new/delete!). This is most likely the reason why the compiler saw these two implementations and chose them over mine, which resided in a .cpp file in a library.
The solution to this was to move the implementation of my overloaded operators to a header file in the library which is directly included from my code.
Related
I replaced operator new with my own implementation. I would like to use my operator new for every call to new in my program except some cases like std containers.
Is it possible to make std container to use the default operator new? For example, std::list.
The short answer is "no".
When any compilation unit replaces an operator new(), it is effectively replaced for all compilation units in the program. That means all usages of or references to that variant of operator new() will resolve to the replacement. The default allocators in the standard containers are affected as much as any other code in the program.
The scheme for replacing operator new() and its variants is all-or-nothing i.e. you don't get some usages of operator new() resolving to the default version and others resolving to the replacement.
You can provide a allocator for your containers that uses some means to obtain raw memory other than your replacement operator new().
How to overload new and delete operator for a dll. I have written overloaded operators as part of the dll , but client linking with this dll does not use overloaded new and delete
Here is what the C++ standard has to say about this, in section 17.6.4.6/3:
The program's definitions (of the new / delete operators) are used instead of the default versions supplied by the implementation. Such replacement occurs prior to program startup. The program's definitions shall not be specified as inline. No diagnostic is required.
If you read that carefully, it exactly explains the trouble you are having. There is a kind of "catch 22" happening here.
On one hand, you cannot have the definitions of your new/delete operators compiled inside the DLL because the overloaded new/delete cannot be linked dynamically (this is because new/delete might be needed during static initialization, prior to loading the DLL, so you would have inconsistent new/delete operators before and after the loading of the DLL, and that's undefined behavior).
On the other hand, you cannot just put your new/delete operator definitions in your DLL header files, because they would need to be marked inline in order to satisfy the One Definition Rule (ODR), which, in turn, doesn't satisfy the above clause. The requirement for them to not be marked inline is probably there because a function definition marked inline has "no linkage", resulting in each translation unit using its own compiled version of it (or as inline expansions), which, normally is OK, but not for dynamic memory allocation.
Both of the above catches are motivated by the fact that, for correctness, there generally needs to be a guarantee that memory allocated with new is deallocated with the corresponding delete operator (i.e., "compiled together", so to speak, or both defaulted). For example, if your new/delete operators rely on an underlying malloc/free call, you rely on the heap used by the translation unit that called the new/delete operator, between a DLL and an executable, there is no guarantee that this heap will be the same (in fact, in Windows, in particular, it is not, the two modules use two separate heaps for dynamic memory allocations).
So, the solution to your problem, as Rook puts it, is "don't do that". Don't overload the new/delete operators for DLL objects because there is no clean method to correctly do this, whichever way you twist and turn your code, it will always boil down to the same problem stated above.
What you can and should do instead is to use a factory function pattern for your DLL objects and return a smart pointer (such as a std::shared_ptr) with a custom deleter that relies on a dynamic dispatching of the deletion back to the site where the object was created. This is inspired by a technique from Chad Austin. I have done something very similar here.
You could try and write your own malloc and delete functions and then create a C definition that basically contains the code to override new and delete and call these custom malloc and delete functions.
This way you could do something such as
MODULE_START()
// CODE HERE
MODULE_END()
and this should work smoothly.
I understand that there are 3 general ways to modify the behaviour of new and delete in C++:
Replacing the default new/delete and new[]/delete[]
Overriding or overloading the placement versions (overriding the one with a memory location passed to it, overloading when creating versions which pass other types or numbers of arguments)
Overloading class specific versions.
What are the restrictions for performing these modifications to the behaviour of new/delete?
In particular are there limitations on the signatures that new and delete can be used with?
It makes sense if any replacement versions must have the same signature (otherwise they wouldn't be replacement or would break other code, like the STL for example), but is it permissible to have global placement or class specific versions return smart pointers or some custom handle for example?
First off, don't confuse the new/delete expression with the operator new() function.
The expression is a language construct that performs construction and destruction. The operator is an ordinary function that performs memory (de)allocation.
Only the default operators (operator new(size_t) and operator delete(void *) can be used with the default new and delete expressions. All other forms are summarily called "placement" forms, and for those you can only use new, but you have to destroy objects manually by invoking the destructor. Placement forms are of rather limited and specialised need. By far the most useful placement form is global placement-new, ::new (addr) T, but the behavior of that cannot even be changed (which is presumably why it's the only popular one).
All new operators must return void *. These allocation functions are far more low-level than you might appreciate, so basically you "will know when you need to mess with them".
To repeat: C++ separates the notions of object construction and memory allocation. All you can do is provide alternative implementations for the latter.
When you overload new and delete within a class you are effectively modifying the way the memory is allocated and released for the class, asking for it to give you this control.
This may be done when a class wants to use some kind of pool to allocate its instances, either for optimisation or for tracking purposes.
Restrictions, as with pretty much any operator overload, is the parameter list you may pass, and the behaviour it is expected to adhere to.
It seems both can be overloaded, but somebody said not.....
What's the case?
It seems you are making the distinction between the handling of new-expressions and allocation functions. new-expressions call constructors in addition for class types and is responsible for looking up allocation functions (so it is built into the compiler) and calling them. You can't change that behavior. What you can change is the behavior of allocation functions. Their name is operator new and they are found and used by new-expressions to allocate memory.
See Plain new, new[], delete and delete[] in a nutshell.
You can overload all four of, new, new[], delete, and delete[]. See section 13.5 of the standard.
You can overlload new operator but you cannot define new operators(like ^ or ++).
If it is not what you meant please clarify.
Why can't it just be regular function calls? New is essentially:
malloc(sizeof(Foo));
Foo::Foo();
While delete is
Foo:~Foo();
free(...);
So why does new/delete end up having it's own syntax rather than being regular functions?
Here's a stab at it:
The new operator calls the operator new() function. Similarly, the delete operator calls the operator delete() function (and similarly for the array versions).
So why is this? Because the user is allowed to override operator new() but not the new operator (which is a keyword). You override operator new() (and delete) to define your own allocator, however, you are not responsible (or allowed to for that matter) for calling appropriate constructors and destructors. These function are called automatically by the compiler when it sees the new keyword.
Without this dichotomy, a user could override the operator new() function, but the compiler would still have to treat this as a special function and call the appropriate constructor(s) for the object(s) being created.
You can overload operator new and operator delete to provide your own allocation semantics. This is useful when you want to bypass the default heap allocator's behavior. For example if you allocate and deallocate a lot of instances of a small, fixed-size object, you may want to use a pool allocator for its memory management.
Having new and delete as explicit operators like other operators makes this flexibility easier to express using C++'s operator overloading mechanism.
For auto objects on the stack, allocation/constructor call and deallocation/destructor calls basically are transparent as you request. :)
'Cause there is no way to provide complie-time type safety with a function (malloc() returns void*, remember). Additionally, C++ tries to eliminate even a slightest chance of allocated but uninitialized objects floating around. And there are objects out there without a default constructor - for these, how would you feed constructor arguments to a function? A function like this would require too much of a special-case handling; easier to promote it to a language feature. Thus operator new.
'new/delete' are keywords in the C++ language (like 'for' and 'while'), whereas malloc/calloc are function calls in the standard C library (like 'printf' and 'sleep'). Very different beasts, more than their similar syntax may let on.
The primary difference is that 'new' and 'delete' trigger additional user code - specifically, constructors and destructors. All malloc does is set aside some memory for you to use. When setting aside memory for a simple plain old data (floats or ints, for example), 'new' and 'malloc' behave very similarly. But when you ask for space for a class, the 'new' keyword sets aside memory and then calls a constructor to initialize that class. Big difference.
Why does C++ have separate syntax for greater-than? Why can't it just be a regular function call?
greaterThan(foo, bar);