Frage

I have read about new and delete overloading for memory tracking in How_To_Find_Memory_Leaks

I defined these global operators:

inline void* __cdecl operator new( unsigned int size, const char *file, int line ) {
    void* ptr = malloc( size );
    AddTrack((DWORD)ptr, size, file, line);
    return ptr;

}

inline void* __cdecl operator new( unsigned int size, void* ptr, const char *file, int line ) {
    return ptr;
}

It works well with new and new[] operators, but i have a problem with placement new ( second one ). My define look like:

#define new new( __FILE__, __LINE__)
#define new(x) new( x, __FILE__, __LINE__)

They work separately. But when i try to use them both there are errors appear. As I understand they substitute each other. I know I can have macro with variable number of arguments like this:

#define new( ... ) new( __VA_ARGS__, __FILE__, __LINE__)

But I need the same macro with and without arguments at all, so both new-s in these lines substitute right:

g_brushes = new Brush[ num_brushes ];
...
new( &g_brushes[i] )Brush(sides);
War es hilfreich?

Lösung

If you decide to walk the dark path of overriding the global new, you have to make sure you consider all of the following scenarios:

new Foo;                            // 1
new Foo[10];                        // 2
new (std::nothrow) Foo;             // 3
new (p) Foo;                        // 4 - placement, not overridable
(Foo*) ::operator new(sizeof(Foo)); // 5 - direct invocation of the operator

And it should be possible to be able to handle all the above except for the last. Shudder.

The necessary knol and the sleight of hand is that your macro should end with new. When your macro ends with new, you can delegate the different ways it can be invoked to new itself.

Here's one, non-thread safe way to proceed. Define a type to capture the context of the invocation, so we will be able to retrieve this context later in the operator itself,

struct new_context {
    new_context(const char* file, const int line)
        : file_(file), line_(line) { scope_ = this; }
    ~new_context() { scope_ = 0; }

    static new_context const& scope() { assert(scope_); return *scope_; }

    operator bool() const { return false; }

    const char* file_;
    const int line_;
private:
    static new_context* scope_;
};

Next, define your override to create a new_context temporary just before the invocation,

#define new new_context(__FILE__, __LINE__) ? 0 : new

using the ternary operator conditional assignment to evaluate an expression before delegating to the operator new, notice that it is where the operator bool that we have defined above comes handy.

Then inside your new overrides (I'll use standard C++98 here instead of the MSC C++), all you have to do is to retrieve the context:

void* operator new (std::size_t size) throw (std::bad_alloc) {
    std::cout 
        << "new" 
        << "," << new_context::scope().file_ 
        << ":" << new_context::scope().line_ 
        << std::endl;
    return 0;
}

This approach will deal with all the cases 1-4 from above, and what's important and can be easily overlooked is that in case 4 where your overloads are not invoked, as you cannot replace placement new (§18.4.​1.3), you still know that placement new took place because new_context will be created and destroyed.


To summarise, you don’t need to modify what follows after the new operator, so all possible syntaxes remain valid. And the other point is that the new_context object temporary is going to be kept alive until the end of the expression the operator participates in, so you are safe to obtain it from a global singleton.


See a gcc example live. Adapting to MSC is left to the reader.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top