c++ 中的重载全局new,delete

分类: c++ 2010-08-06 10:31 116人阅读 评论(1) 收藏 举报

最近做一个小项目,对c++又有很多新的理解。实在不的不让人发出感叹,c++太强大了,绝对不是一朝一夕就可以领悟她的内涵的。

首
先我们要清楚,为什么我们要重载new,和delete了?这还不是指针造成的,确实指针是一件让人喜欢的东西,用起来如此让人喜欢,让人顺手。然而小程
序我们完全可以避免内存泄露问题,大程序就不那么容易了,然而我们有一种特别好的方法可以跟踪我们new,和delete动作,找到未被释放的内存。办法
是什么呢?微软给我们提供了一种很好的方法,那就是重载new,和delete。

在实现之前我们要清楚new,和delete的工作机理,这是个多么好的学习机会啊!

对
与new操作符,其实和sizeof一样,都是c++内置的,然而像strlen就不是了,strlen属于函数。对于new的功能我们是没有办法改变
的,当我们new一个对象时,new为我们做了两件事情,一、申请一块足够的内存空间供存放对象,对于new一个数组对象,编译器会计算出总共的空间,然
后执行类似c语言中malloc函数类似的功能。二、初始化对象,对于单个对象,包括包括基本对象和类对象,可以通过括号初始化,比如int * pn =
new int(3); A * pa = new
A(3);   然而对于数组不能初始化,对于类对象,必须定义无参数的构造函数。对与new的基本功能我们是无法改变的。

我们所能改变的就是如何为对象分配内存,我们可一重载这个函数以实现分配内存。通常的重载方式是:

void * operator new (size_t size);

然而这已经足够了,在一般的operator new 重载函数里,我们可以再加入其它参数,但第一个参数必须是size_t类型,即为无符号整形。正是有这种特性,为我们对内存申请和释放的跟踪提供了可能。

具体实现就是第一个operator new 的重载函数,我们第一的这个函数是这样的:

void * operator new(unsigned int size, const char *file, int line)
{
    cout << "new size:" << size << endl;
    cout << file << " " << line << endl;

void * p = malloc(size);

return p;
}

然后用宏替换所有的new:

#define new new(__FILE__, __LINE__)

这
样我们每次调用new,比如int * pn = new int;被编译器替换成了int * pn = new (__FILE__,
__LINE__) int,从而调用我们定义的operator
new,这种办法确实很妙。需要交代的是,对于数组同样适用,而是在编译的是后由编译器计算出所需要的长度调用我们定义的operator new函数。

对于delete,我们无需使用宏定义,只要重载operator delete就可以了,然而我们需要重载两个delete:

void operator delete(void * p)
{
    cout << "delete " << (int)p << endl;
    free(p);
}

void operator delete [] (void * p)
{
    cout << "delete [] " << (int)p << endl;
    free(p);
}

其
实后面一个函数的内容和前面的内容一样,但为了支持数组的释放,我们必须是要定义的。那么我们只是简单的free(p)编译器咋知道我们释放的数组,还是
单个对象了,这个不用我们操心了,我们只要提供给free函数一个申请内存的首地址就可以了。因为在用malloc申请的时候,我们也把数组装换为内存大
小了。

由此我们可以得出下面的推断:用int * pn = new int [100];的空间,用delete
pn释放和delete [] pn释放效果一样。然而那么既然这两种方式一样,那还要delete [] 干什么,其实delete []
有另外的作用就是类对象数组的释放,编译器把delete []解释为调用对象的析构函数,这个是通过循环实现的,最后释放掉申请的内存。

既
然我们已经跟踪了new和delete,那么就可以比较容易的判断申请的内存是否最后得到释放,要完成它,我们还需要一个链表,或者其它,当我们申请一块
内存的时候加入到链表中,释放一块空间的时候,从链表中删除和释放内存首地址相同的节点就可以了,最后理想的情况是链表为空,如果不为空,那就说明内存发
生泄露(Memory leaks)了。

完整代码:

  1. #include "malloc.h"
  2. #include "iostream.h"
  3. #ifdef _DEBUG
  4. void * operator new(unsigned int size, const char *file, int line)
  5. {
  6. cout << "new size:" << size << endl;
  7. cout << file << " " << line << endl;
  8. // 下面两种方法可以达到同样的效果,但下面一种比较好
  9. // 因为用下面一种可以保持原有的申请方式一样
  10. //void * p = malloc(size);
  11. void * p = operator new(size);
  12. return p;
  13. }
  14. void operator delete(void * p)
  15. {
  16. cout << "delete " << (int)p << endl;
  17. free(p);
  18. }
  19. void operator delete [] (void * p)
  20. {
  21. cout << "delete [] " << (int)p << endl;
  22. free(p);
  23. }
  24. void operator delete(void * p, const char *file, int line)
  25. {
  26. cout << "delete file line" << endl;
  27. free(p);
  28. }
  29. void operator delete [] (void * p, const char *file, int line)
  30. {
  31. cout << "delete [] file line" << endl;
  32. free(p);
  33. }
  34. #define new new(__FILE__, __LINE__)
  35. #endif
  36. void main()
  37. {
  38. int * p = new int[5];
  39. delete [] p;
  40. // delete p;
  41. }
 
#include "malloc.h"
#include "iostream"
#include "stdlib.h"
#include "stdio.h" using namespace std;
#define _DEBUG #ifdef _DEBUG void * operator new[](std::size_t size, const char *file, int line)
{
cout << "new[] size:" << size << endl;
cout << file << " " << line << endl;
// 下面两种方法可以达到同样的效果,但下面一种比较好
// 因为用下面一种可以保持原有的申请方式一样
//void * p = malloc(size);
void * p = operator new(size);
return p;
} void * operator new(std::size_t size, const char *file, int line)
{
cout << "new size:" << size << endl;
cout << file << " " << line << endl;
// 下面两种方法可以达到同样的效果,但下面一种比较好
// 因为用下面一种可以保持原有的申请方式一样
//void * p = malloc(size);
void * p = operator new(size);
return p;
}
void operator delete(void * p)
{
//cout << "delete " << reinterpret_cast<int>(p) << endl;
cout << "delete " << p << endl;
free(p);
}
void operator delete [] (void * p)
{
//cout << "delete [] " << static_cast<int> ( p ) << endl;
cout << "delete [] " << p << endl;
free(p);
}
void operator delete(void * p, const char *file, int line)
{
cout << "delete file line" << endl;
free(p);
}
void operator delete [] (void * p, const char *file, int line)
{
cout << "delete [] file line" << endl;
free(p);
}
#define new new(__FILE__, __LINE__)
#endif
int main()
{
int * p = new int[5];
delete [] p; int * p2 = new int;
delete p2; return 0;
}

end$ ./1
new[] size:20
newoverload.cpp 58
delete [] 0x23b4010
new size:4
newoverload.cpp 61
delete 0x23b4010

 
 
 
 

ZT c++ 中的重载全局new,delete的更多相关文章

  1. 重载全局new/delete实现内存检测

    下面介绍用重载new/delete运算符的方式来实现一个简单的内存泄露检测工具,基本思想是重载全局new/delete运算符,被检测代码调用new和delete运算符时就会调用重载过的operator ...

  2. C++:重载全局new/delete实现跨平台多线程内存检测

    Reference: https://blog.csdn.net/u014023615/article/details/39551191 Reference: https://blog.csdn.ne ...

  3. 重载new和delete来检测内存泄漏

    重载new和delete来检测内存泄漏 1. 简述 内存泄漏属于资源泄漏的一种,百度百科将内存泄漏分为四种:常发性内存泄漏.偶发性内存泄漏.一次性内存泄漏和隐式内存泄漏.    常发性指:内存泄漏的代 ...

  4. 重载operator new delete函数

    可以重载global的operator new delete 函数,细节如下: MyNewDelete.h #pragma once #include <stdlib.h> #includ ...

  5. 关于C++中操作符重载的疑问 :四个运算符=, -&gt;, [], ()不可以重载为全局函数(友员函数)

    转载自:http://blog.csdn.net/u014610226/article/details/47679323     以下是对C++中不能重载为友元函数的四个运算符进行了详细的分析介绍,需 ...

  6. C++重载new和delete运算符

    内存管理运算符 new.new[].delete 和 delete[] 也可以进行重载,其重载形式既可以是类的成员函数,也可以是全局函数.一般情况下,内建的内存管理运算符就够用了,只有在需要自己管理内 ...

  7. C++学习32 重载new和delete运算符

    内存管理运算符 new.new[].delete 和 delete[] 也可以进行重载,其重载形式既可以是类的成员函数,也可以是全局函数.一般情况下,内建的内存管理运算符就够用了,只有在需要自己管理内 ...

  8. c/c++ 重载new,delete运算符 placement new

    重载new,delete运算符 new,delete在c++中也被归为运算符,所以可以重载它们. new的行为: 先开辟内存空间 再调用类的构造函数 开辟内存空间的部分,可以被重载. delete的行 ...

  9. C/C++基础----特殊工具和技术 (重载new和delete,RTT,限定作用域的枚举类型,类成员指针,嵌套类,局部类,volatile,链接指示 extern “C”)

    重载new和delete 1调用operator new( 或new[])标准库函数分配足够大的.原始的.未命名的内存空间以便存储特定类型的对象 2编译器运行相应地构造函数以构造这些对象,并为其传入初 ...

随机推荐

  1. Codeforces - 631B 水题

    注意到R和C只与最后一个状态有关 /*H E A D*/ struct node2{ int kind,las,val,pos; node2(){} node2(int k,int l,int v,i ...

  2. 【研究】Struts2-052漏洞

    漏洞描述: CVE-2017-9805 2017年9月5日,Apache Struts官方发布最新的安全公告称,Apache Struts 2.5.x的REST插件存在远程代码执行高危漏洞,漏洞编号为 ...

  3. 搭建Flask+Vue及配置Vue 基础路由

    最近一直在看关于Python的东西,准备多学习点东西.以前的项目是用Vue+Java写的,所以试着在升级下系统的前提下.能不能使用Python+Vue做一遍. 选择Flask的原因是不想随大流,并且比 ...

  4. java移位操作注意事项

    来个浅显的 在java中,移位分为有符号移位和无符号移位,无符号移位的话不论左移还是右移,自动在低位或者高位补0 如果是有符号数进行移位的话,必须要先将

  5. java集合常用操作

    收集一些常用集合操作的代码,用于治疗健忘症,:) set转list //构造Map数据 Map<String, String> map = new HashMap<String, S ...

  6. xsd表示byte[]的类型

    byte[]对应xs:base64Binary http://stackoverflow.com/questions/5912526/representing-byte-array-as-an-xsd ...

  7. 【Unity&独立游戏&音效】免费音效网站总览

    转载 http://blog.csdn.net/BuladeMian/article/details/70240868

  8. SpringBoot 之 打war包

    1.修改打包方式为 war <packaging>war</packaging> 2. 修改tomcat 依赖 <dependency> <groupId&g ...

  9. jqgrid 操作

    1.获取单个id 获取行号,有这种方式: var rowid = $("#gridList").jqGrid("getGridParam", "sel ...

  10. 网页设计,Access入门 2010,语文

    Access入门 2010(select查询) order by(按升序排列)?+desc(按降序排列) group by(进行分组,下一行只能用having) sum(数值总数)?+as?(新字段的 ...