宏替换C ++运算符new

时间:2009-03-06 16:30:35

标签: c++ gcc c-preprocessor

是否可以创建宏来替换包含其他参数的重载的所有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关于为什么这是不可能的,但我觉得我是如此亲密,以至于必须有办法。有什么明显的东西我不见了吗?

7 个答案:

答案 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替​​换功能

     
      
  1. 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&)
    •   
  2.   

希望这可以澄清什么是法律超载,什么不是。

这可能会引起一些人的兴趣:

#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运算符来替换内存分配器,但不能注入您正在讨论的特殊变量。