ZT c++ 中的重载全局new,delete
c++ 中的重载全局new,delete
最近做一个小项目,对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)了。
完整代码:
- #include "malloc.h"
- #include "iostream.h"
- #ifdef _DEBUG
- void * operator new(unsigned int 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 " << (int)p << endl;
- free(p);
- }
- void operator delete [] (void * p)
- {
- cout << "delete [] " << (int)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
- void main()
- {
- int * p = new int[5];
- delete [] p;
- // delete p;
- }
#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的更多相关文章
- 重载全局new/delete实现内存检测
下面介绍用重载new/delete运算符的方式来实现一个简单的内存泄露检测工具,基本思想是重载全局new/delete运算符,被检测代码调用new和delete运算符时就会调用重载过的operator ...
- C++:重载全局new/delete实现跨平台多线程内存检测
Reference: https://blog.csdn.net/u014023615/article/details/39551191 Reference: https://blog.csdn.ne ...
- 重载new和delete来检测内存泄漏
重载new和delete来检测内存泄漏 1. 简述 内存泄漏属于资源泄漏的一种,百度百科将内存泄漏分为四种:常发性内存泄漏.偶发性内存泄漏.一次性内存泄漏和隐式内存泄漏. 常发性指:内存泄漏的代 ...
- 重载operator new delete函数
可以重载global的operator new delete 函数,细节如下: MyNewDelete.h #pragma once #include <stdlib.h> #includ ...
- 关于C++中操作符重载的疑问 :四个运算符=, ->, [], ()不可以重载为全局函数(友员函数)
转载自:http://blog.csdn.net/u014610226/article/details/47679323 以下是对C++中不能重载为友元函数的四个运算符进行了详细的分析介绍,需 ...
- C++重载new和delete运算符
内存管理运算符 new.new[].delete 和 delete[] 也可以进行重载,其重载形式既可以是类的成员函数,也可以是全局函数.一般情况下,内建的内存管理运算符就够用了,只有在需要自己管理内 ...
- C++学习32 重载new和delete运算符
内存管理运算符 new.new[].delete 和 delete[] 也可以进行重载,其重载形式既可以是类的成员函数,也可以是全局函数.一般情况下,内建的内存管理运算符就够用了,只有在需要自己管理内 ...
- c/c++ 重载new,delete运算符 placement new
重载new,delete运算符 new,delete在c++中也被归为运算符,所以可以重载它们. new的行为: 先开辟内存空间 再调用类的构造函数 开辟内存空间的部分,可以被重载. delete的行 ...
- C/C++基础----特殊工具和技术 (重载new和delete,RTT,限定作用域的枚举类型,类成员指针,嵌套类,局部类,volatile,链接指示 extern “C”)
重载new和delete 1调用operator new( 或new[])标准库函数分配足够大的.原始的.未命名的内存空间以便存储特定类型的对象 2编译器运行相应地构造函数以构造这些对象,并为其传入初 ...
随机推荐
- 关于webpack 以及 webpack配置和常用插件的学习记录 (2) ------ devServer
DevServer: devserver会启动一个http服务器用于服务网页请求,接收webpack发出的文件变化的信号.通过websocket协议自动刷新网页,实现实时预览. 安装: npm i w ...
- DVWA1.9平台XSS小结
LOW级别就不写了...... 直接上中高级别(结合源码更好理解) 1.XSS Reflected(Medium) 从源码中可以清楚的看到,输入的<script>标签被过滤掉了,看清了,只 ...
- C++ GUI Qt4编程(04)-2.1findDialog
finddialog.h /* * 未实现findNextSignal和findPreviousSignal */ #ifndef FINDDIALOG_H #define FINDDIALOG_H ...
- 如何在新导入的python项目中一次性生成依赖的第三方库
requirements.txt用来记录项目所有的依赖包和版本号,只需要一个简单的pip命令就能完成. pip freeze >requirements.txt 然后就可以用 pip insta ...
- linux对于zombie的处理
@(Linux基础)[僵尸进程处理] 今天在服务器上推送项目的时候,突然发现很卡.就用top查看了一下,果然此事不简单啊. top - 10:39:16 up 20 days, 23:11, 2 us ...
- npm是什么NPM的全称是Node Package Manager
npm是什么NPM的全称是Node Package Manager
- 记一次MongoDB性能问题
下面文章转载自火丁笔记,原作者描述了一次MongoDB数据迁移过程中遇到的性能问题及其解决方案,中间追查问题的方法和工具值得我们学习.下面是其原文: 最近忙着把一个项目从MySQL迁移到MongoDB ...
- Java入门系列-08-选择结构
这篇文章为你搞懂2个问题 if-else选择结构的使用? switch 的使用? 前面我们学习的代码都是直上直下的执行,还不会"拐弯",这篇文章带大家来看一下会"拐弯&q ...
- VMWare启动虚拟机失败,提示锁定文件失败解决方法
1.问题描述:未正常关闭虚拟机,重新启动时,VMWare启动虚拟机失败 2.解决方法: ①找到该虚拟系统所在的目录,即弹出框中的目录,在目录中找到Windows XP Professional.vmx ...
- MySQL -U防止人为误操作
在很多时候操作数据库的时候,可能领导或DBA登陆了数据库,在执行update和delete时,忘记了加where,可能会导致清空表的悲剧,所以-U的好处就体现了. 1.mysql -U的帮助说明 -U ...