Макросменить оператор С++ new
Можно ли создавать макросы, чтобы заменить все формы operator new
на перегрузки, содержащие дополнительные аргументы... скажем __FILE__
и __LINE__
?
Проблема заключается в том, что operator new
может быть закодирован с круглыми скобками или без них, поэтому:
-
объектно-ориентированные макросы:
#define new new(__FILE__, __LINE__)
заменит объявления типа:
A* a = new A();
-
и макрос функций:
#define new(A) new (A, __FILE__, __LINE__)
заменит объявления типа:
A* a = new(std::nothrow) A();
К сожалению, это ошибка при попытке объявить два макроса с одинаковым идентификатором, даже если они имеют разные типы, поэтому следующее сбой:
#define new new(__FILE__, __LINE__)
#define new(A) new (A, __FILE__, __LINE__) // Error: "new" already defined
Поскольку я использую g++, я надеялся, что использование их синтаксиса variadic macros даст успех, но, к сожалению, нет. Следующее:
#define new(...) new(__FILE__, __LINE__, ## __VA_ARGS__)
соответствует только new(xyx) A()
, а не new A()
.
Я знаю, что были написаны эссе о том, почему это невозможно, но я чувствую, что я настолько близок, что должно быть путь. Есть ли что-то очевидное, что мне не хватает?
Ответы
Ответ 1
Вот что я использую:
В файле new.cpp
const char* __file__ = "unknown";
size_t __line__ = 0;
void* operator new(size_t size) {
void *ptr = malloc(size);
record_alloc(ptr,__file__,__line__);
__file__ = "unknown";
__line__ = 0;
return ptr;
}
void delete(void *ptr)
{
unrecord_alloc(ptr);
free(ptr);
}
Для компактности я оставляю другие определения new и delete. "record_alloc" и "unrecord_alloc" - это функции, которые поддерживают связанный список структуры, содержащий ptr, строку и файл).
в new.hpp
extern const char* __file__;
extern size_t __line__;
#define new (__file__=__FILE__,__line__=__LINE__) && 0 ? NULL : new
Для g++ "новый" расширяется только один раз. Ключом является "& & 0", что делает его ложным и приводит к использованию нового нового. Например,
char *str = new char[100];
расширяется препроцессором до
char *str = (__file__="somefile.c",__line__=some_number) && 0 ? NULL : new char [100];
Таким образом, записываются номер файла и строки и вызывается ваша пользовательская новая функция.
Это работает для любой формы new - при наличии соответствующей формы в new.cpp
Ответ 2
Вы должны проверить эту замечательную запись в блоге моим коллегой Кальвином. Недавно у нас была ситуация, когда мы хотели включить этот тип исправлений, чтобы связать утечки памяти с линией, выделенной ими в сборках диагностики/отладки. Это интересный трюк
http://blogs.msdn.com/calvin_hsia/archive/2009/01/19/9341632.aspx
Ответ 3
3.7.4 Динамическая длительность хранения
2 Библиотека предоставляет определения по умолчанию для глобальных функций распределения и освобождения. Некоторые функции глобального распределения и освобождения являются сменными (18.5.1). Программа С++ должна предоставлять не более одного определения сменной функции распределения или освобождения. Любое такое определение функции заменяет версию по умолчанию, предоставленную в библиотеке (17.6.4.6) [...]
17.6.4.6 Функции замены
-
Программа на С++ может предоставить определение для любого из восьми распределений динамической памяти сигнатуры функций, объявленные в заголовке (3.7.4, раздел 18):
- operator new (std:: size_t)
- operator new (std:: size_t, const std:: nothrow_t &)
- operator new [] (std:: size_t)
- operator new [] (std:: size_t, const std:: nothrow_t &)
- оператор delete (void *)
- оператор delete (void *, const std:: nothrow_t &)
- оператор delete [] (void *)
- оператор delete [] (void *, const std:: nothrow_t &)
Надеюсь, что это разъясняет, что такое законная перегрузка, а что нет.
Это может представлять интерес для нескольких здесь:
#define delete cout << "delete called at: " << __LINE__ << " of " << __FILE__ << endl, delete
using namespace std;
void *operator new(size_t size, ostream& o, char *f, unsigned l) {
o << "new called at: " << l << " of " << f << endl;
return ::new char[size];
}
int main() {
int *a = new(cout, __FILE__, __LINE__) int;
delete a;
}
Caveat Lector: то, что я делаю здесь, - это Bad Thing (TM) - перегрузка нового/удалить глобально.
Ответ 4
Вы не говорите, какой компилятор вы используете, но, по крайней мере, с GCC, вы можете переопределить новый и зарегистрировать адрес вызывающего абонента, а затем перевести его в файл/строку с помощью addr2line (или использовать библиотеку BFD для этого немедленно).
Ответ 5
Я нашел следующую библиотеку " nvwa", очень полезная для отслеживания утечек памяти new/delete - взгляните на файл "debug_new" для примеров или просто используйте его как есть.
Ответ 6
Что вы можете сделать, это перегрузить оператор new и получить там трассировку стека (конкретную платформу) и использовать информацию стека для вывода из того, откуда был вызван новый.
Ответ 7
Нет, нет способа.
Вы можете сделать это в плохие старые дни malloc()/free()
, но не для new
.
Вы можете заменить распределитель памяти глобальным переопределением оператора new
, но вы не можете вводить специальные переменные, о которых вы говорите.