问题
I run the following code on VS2005:
#include <iostream>
#include <string>
#include <new>
#include <stdlib.h>
int flag = 0;
void* my_alloc(std::size_t size)
{
flag = 1;
return malloc(size);
}
void* operator new(std::size_t size) { return my_alloc(size); }
void operator delete(void* ptr) { free(ptr); }
void* operator new[](std::size_t size) { return my_alloc(size); }
void operator delete[](void* ptr) { free(ptr); }
int main()
{
std::string str;
std::getline(std::cin, str);
std::cout << str;
return flag;
}
I enter a long enough string (longer than the small-string-optimization buffer):
0123456789012345678901234567890123456789012345678901234567890123456789
In a Debug compilation the process returns 1, in Release configuration the process returns 0, which means that the new operator isn't called! I can verify this by putting a breakpoint, writing to output/debug output, etc...
Why is this, and is it a standard conforming behavior?
回答1:
After some research, what @bart-jan wrote in his second answer (which is now deleted although no-one downvoted it) is in fact correct.
As it can be easily seen mine operator isn't called in Release at all, instead the CRT version is called. (And no, for all those who shot in the dark, there's no recursion here.) The question is "why?"
The above was compiled against the dynamically linked CRT (which is the default). Microsoft provides an instantiation of std::string (among many other standard templates) within the CRT DLL. Looking into Dinkumware's headers shipped with VS2005:
#if defined(_DLL_CPPLIB) && !defined(_M_CEE_PURE)
template class _CRTIMP2_PURE allocator<char>;
// ...
template class _CRTIMP2_PURE basic_string<char, char_traits<char>,
allocator<char> >;
Where _CRTIMP2_PURE
expands to __declspec(dllimport)
. That means that in Release the linker links the std::string
to the version that was instantiated when the CRT was built, which uses the default implementation of new
.
It's unclear why it doesn't happen in debug. As @Violet Giraffe guessed correctly it must be affected by some switches. However, I think it's the linker switches, not the compiler switches. I can't find what exactly switch matters.
The other left question which everyone here ignored is "is it standard"? Trying the code in VS2010, it indeed called my operator new
no matter what configuration I compile! Looking in the headers shipped with VS2010 reveals that Dinkumware removed the __declspec(dllimport)
for the above instantiation. Thus, I believe the old behavior is indeed a compiler bug, and not standard.
回答2:
Calling cout << std::string() from your new operator or one of the called functions within, will cause unpredicable program behavour. Don't use io from your new operator. Doing things like that may re-enter your new operator.
回答3:
You really should debug the code step-by-step. Did you try setting /Od in your release configuration to disable optimization? I wonder if that changes the behaviour.
回答4:
You have infinite recursion.
Instead, do like
#include <iostream>
#include <string>
#include <new>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
void* my_alloc( char const* str, std::size_t size )
{
fprintf( stderr, "%s %lu\n", str, (unsigned long)size );
return malloc( size ); // I don't care for errors here
}
void* operator new( std::size_t size ) { return my_alloc( "new", size ); }
void operator delete( void* ptr ) { free(ptr); }
void* operator new[]( std::size_t size ) { return my_alloc( "new[]", size ); }
void operator delete[]( void* ptr ) { free( ptr ); }
int main()
{
std::string str;
std::cout << "? ";
std::getline(std::cin, str);
std::cout << str;
}
来源:https://stackoverflow.com/questions/8257236/why-the-overrided-operator-new-isnt-call