是否可以创建宏来替换包含其他参数的重载的所有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()
。
我知道essays have been written关于为什么这是不可能的,但我觉得我是如此亲密,以至于必须有办法。有什么明显的东西我不见了吗?
答案 0 :(得分:23)
以下是我使用的内容:
在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,line和file的结构的链表的函数。
在new.hpp中
extern const char* __file__;
extern size_t __line__;
#define new (__file__=__FILE__,__line__=__LINE__) && 0 ? NULL : new
对于g ++,“new”只展开一次。关键是“&& 0”,它使它成为假并导致使用真正的新。例如,
char *str = new char[100];
由预处理器扩展为
char *str = (__file__="somefile.c",__line__=some_number) && 0 ? NULL : new char [100];
因此会记录文件和行号,并调用您的自定义新函数。
这适用于任何形式的新 - 只要new.cpp中有相应的表单
答案 1 :(得分:8)
你应该看看我的同事凯文的这篇优秀博客文章。我们最近有一种情况,我们想要启用这种类型的修复,以便将内存泄漏与在诊断/调试版本中分配它们的行相关联。这是一个有趣的技巧
http://blogs.msdn.com/calvin_hsia/archive/2009/01/19/9341632.aspx
答案 2 :(得分:4)
3.7.4动态存储时间
2该库提供全局分配和释放功能的默认定义。一些全局分配和释放功能是可替换的(18.5.1)。 C ++程序最多只能提供一个可替换分配或释放功能的定义。任何此类函数定义都将替换库中提供的默认版本(17.6.4.6)[...]
17.6.4.6替换功能
- 醇>
C ++程序可以为八个动态内存分配中的任何一个提供定义 标题中声明的函数签名(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&)
- operator delete(void *)
- operator delete(void *,const std :: nothrow_t&)
- operator delete [](void *)
- operator 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;
}
警告Lector :我在这里做的是Bad Thing(TM) - 全局重载新/删除。
答案 3 :(得分:1)
您没有说明您正在使用哪种编译器,但至少使用GCC,您可以覆盖new并记录调用者地址,然后使用addr2line将其转换为文件/行信息(或使用BFD库执行此操作)立即)。
答案 4 :(得分:1)
我发现以下库“nvwa”对于跟踪新的/删除内存泄漏非常有用 - 请查看文件“debug_new”以获取示例,或者只是“按原样”使用它。
答案 5 :(得分:1)
你可以做的是重载操作符new并在那里获取堆栈跟踪(特定于平台)并使用堆栈信息推断出新调用的位置。
答案 6 :(得分:-2)
不,没有办法。
你可以在malloc()/free()
过去的糟糕日子里做到这一点,但不能用于new
。
您可以通过全局覆盖new
运算符来替换内存分配器,但不能注入您正在讨论的特殊变量。