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编译器运行相应地构造函数以构造这些对象,并为其传入初 ...
随机推荐
- UESTC - 1652 递推方程
方程很简单,每一公里往上推就行 WA了2发,忘了单通道时的特判,还有n m傻傻分不清,忘了fixed什么的我好弱啊QAQ.. #include<bits/stdc++.h> #define ...
- pip安装时的异常,找不到lib2to3\\Grammar.txt
[From] http://jahu.iteye.com/blog/2353325 异常 : [Errno 2] No such file or directory: 'd:\\python\\pyt ...
- jenkins显示发送邮件发送成功但是邮箱没收到
jenkins显示发送邮件发送成功但是邮箱没收到 解决方案: 重新配置一下系统管理-系统设置-Extended E-mail Notification
- C语言实现排序
//C语言版排序#include<stdio.h> #include<stdlib.h> //冒泡排序 void bubleSort(int data[], int n); / ...
- java中的线程(2):如何正确停止线程之3种常见停止方式
1.常见停止方式 自定义线程,其中含退出标志位,在run中判断它. 使用interrupt()方法中断线程 使用stop方法暴力终止(已经弃用) 2.使用标志位 class TestThread ex ...
- jquery将日期转换成指定格式的字符串
引用jquery文件,如<script type="text/javascript" src="jquery-1.8.3.min.js"></ ...
- javascript中构造函数与普通函数的区别还有关于“new”操作符的一些原理
有一种创建对象的方法叫做工厂模式,例如: function person(name,age){ var o=new Object(); o.name=name; o.age=age; return o ...
- strus2配置strus.xml问题-The content of element type "package" must match "(result-types?,interceptors?
搭建strus2项目,在配置strus.xml时候碰到了这个问题: The content of element type "package" must match "( ...
- SQL手工注入学习 一
sql注入: (基于DVWA环境的sql注入) 流程: 1.判断是否有SQL注入漏洞 2.判断操作系统.数据库和web应用的类型 3.获取数据库信息看,包括管理员信息(拖库) ...
- android检查网络连接状态的变化,无网络时跳转到设置界面
在AndroidManifest.xml中加一个声明<receiver android:name="NetCheckReceiver"> <intent-filt ...