Обнаружение утечки памяти и переопределение нового?

Я пытаюсь заставить работать обнаружение утечки памяти с помощью этих двух статей: http://msdn.microsoft.com/en-us/library/e5ewb1h3%28VS.80%29.aspx http://support.microsoft.com/kb/q140858/

Итак, в моем stdafx.h теперь есть:

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>

#define new new(_NORMAL_BLOCK,__FILE__,__LINE__)

Единственная проблема в том, что у меня есть класс, который переопределяет новую функцию:

class Dummy
{    
  //overloaded new operator
  void FAR* operator new(size_t cb);
}

Теперь, когда я компилирую этот код, я получаю: ошибка C2059: синтаксическая ошибка: «постоянная» ошибка C2091: функция возвращает функцию

Любая идея, как я могу это исправить?


person Kyle    schedule 14.07.2009    source источник
comment
Проблема с макросами :(   -  person GManNickG    schedule 15.07.2009
comment
Если бы вы не использовали окна, я бы предложил valgrind.   -  person LiraNuna    schedule 15.07.2009
comment
Я бы использовал DUMA, если бы использовал Windows.   -  person EFraim    schedule 15.07.2009


Ответы (4)


Вы можете использовать директивы pragma для сохранения и восстановления нового макроса при отмене определения для перегрузок. См. [MSDN](http://msdn.microsoft.com/en-us/library/hsttss76(VS.71).aspx) для точного синтаксиса.

E.g.

#pragma push_macro("new")
#undef new
void FAR* operator new(size_t cb);
#pragma pop_macro("new") 

Вы можете поместить их в заголовки, например.

begin_new_override.h:

#ifdef new
#define NEW_WAS_DEFINED
#pragma push_macro("new")
#undef new
#endif

end_new_override.h:

#ifdef NEW_WAS_DEFINED
#undef NEW_WAS_DEFINED
#pragma pop_macro("new")
#endif

А потом

#include "begin_new_override.h"
void FAR* operator new(size_t cb);
#include "end_new_override.h"
person Logan Capaldo    schedule 14.07.2009
comment
Круто, я и не знал, что такое возможно :-) - person Mark Simpson; 09.12.2015

По моему опыту, переопределение new через #define на уровне препроцессора - плохая идея - вы не только нарушаете перегрузку operator new, но и размещение new, и, возможно, несколько других вещей.

Наличие всех этих макросов FILE и LINE, расширяющихся повсюду, приводит к тому, что ваши разделы .rodata и .data раздуваются строками файлов и номерами строк, и генерирует гораздо больше кода за вызов.

Гораздо лучше (если заранее приложить больше усилий) воспользоваться наличием отладочной информации (например, файл .pdb) и использовать что-то вроде DbgHelp. library/ms680650(VS.85).aspx" rel="nofollow noreferrer">StackWalk64 для сбора информации о стеке.

Перегружайте различные комбинации глобального оператора new и оператора delete (массив, nothrow и т. д.), чтобы они сохраняли и освобождали информацию о стеке по мере выделения и освобождения памяти.

Вы даже можете хранить эту информацию в такой структуре, как std::map‹void *, StackInfo›, просто будьте осторожны, чтобы не записывать аллоки, вызванные вставками карты (глобальная блокировка может быть достаточной для однопоточного приложения, многопоточного приложения). -threaded оставляем читателю в качестве упражнения).

Поскольку вы записываете весь стек для любого заданного распределения, вы можете провести изящный анализ дерева, сгруппировав выделения (утечки или иным образом) по «функциям и потомкам»... И иногда проще отследить сложные утечки, если вы знаете весь стек с момента их выделения.

person leander    schedule 15.07.2009
comment
Между прочим, если кто-то уже знает о библиотеке/наборе инструментов для подобных вещей (хранение информации о распределении с трассировкой стека) под Windows - за исключением коммерческих решений - я бы хотел услышать об этом и с радостью обновлю ответ. Нет причин изобретать велосипед. Версия этого, которую я использовал, была на встроенной платформе... - person leander; 15.07.2009
comment
Попробуйте просмотреть трассировки ETW с окнами. Инструменты бесплатные и небольшие, поэтому их можно установить на клиентские машины для облегчения отладки. Также вы можете сами размещать дополнительную информацию в трассировках etw из своего приложения. они могут отслеживать выделение и освобождение памяти и собирать информацию о стеке для каждого из них. Самое сложное в этом то, что настройка WPA для отображения нужной информации может быть пугающей. Здесь есть несколько полезных видео, которые помогут вам начать работу: randomascii.wordpress.com/2014/08/19/ - person 0xC0DEFACE; 24.03.2016

Вместо того, чтобы определять new как что-то другое, почему бы не перегрузить оператор new?

Добавьте эти определения функций где-нибудь в глобальном пространстве имен:

// operator new overloads
void* operator new( const size_t size, const char* file, int line) throw();
void* operator new( const size_t size, const size_t align, const char* file, int line) throw();
void* operator new[]( const size_t size, const char* file, int line) throw();
void* operator new[]( const size_t size, const size_t align, const char* file, int line) throw();

// can't easily overload operator delete
void operator delete( void* ptr ) throw();
void operator delete[]( void* ptr ) throw();

// matched to the operator new overload above in case of exceptions thrown during allocation
void operator delete( void* ptr, const char* file, int line) throw();
void operator delete[]( void* ptr, const char* file, int line) throw();
void operator delete( void* ptr, const size_t align, const char* file, int line) throw();
void operator delete[]( void* ptr, const size_t align, const char* file, int line) throw();

// global new/delete
void* operator new( size_t size ) throw();
void* operator new( size_t size, const std::nothrow_t& ) throw();
void* operator new( size_t size, size_t align ) throw();
void* operator new( size_t size, size_t align, const std::nothrow_t& ) throw();

void* operator new[]( size_t size ) throw();
void* operator new[]( size_t size, const std::nothrow_t& ) throw();

void operator delete( void* ptr, const std::nothrow_t&) throw();
void operator delete[]( void* ptr, const std::nothrow_t&) throw();

Затем вы можете определить свой собственный новый макрос, который вызывает неглобальные версии, и реализовать глобальные версии, чтобы утверждать или предупреждать, если они вызываются (чтобы поймать что-либо проскальзывающее).

#define MY_NEW(s)    new(s, __FILE__, __LINE__)

Ваши перегрузки на уровне класса будут работать должным образом, если вы вызовете «new» непосредственно в классе. Если вы хотите вызвать MY_NEW для класса, вы можете это сделать, но вам придется переопределить перегрузку в классе, чтобы она соответствовала вашей новой.

person dma    schedule 14.07.2009
comment
__LINE__ это int, а не char*. - person Cornstalks; 26.09.2012
comment
Помогли мне понять конкретные перегрузки для операторов new и delete. - person Vinz; 24.02.2016

Попробуйте #undef new перед определением класса, а затем снова #define new new... после.

person EFraim    schedule 14.07.2009