详解new/delete(整合)
C++中内存的动态分配与管理永远是一个让C++开发者头痛的问题,本文通过对C++中内存的动态分配释放的基本原理的介绍,让读者朋友能对C++中的内存的动态分配与释放有较为深入的理解,从而更好驾驭C++程序。
1. 函数(Function)
(1) operator new function
void * ::operator new(size_t); //Global
void * class-name::operator new(size_t); //Class
上面是C++中operator new function的原型,一个是全局类型的,一个的类成员类型的。全局类型的operator new函数在下面两种情况下被调用:一种是在分配C++内建(built-in)类型的动态内存时,一种是在分配用户没有自己定义operator new成员函数的用户自定义类型的动态内存时。 如果用户在自己定义的类型中,定义了operator new函数,那么用户在用new申请该类型的动态内存时, 便会调用该类型的成员函数operator new, 而不是全局的operator new。
另外,我们注意到,上面的原型中函数的返回值为void *类型, 第一个参数为size_t类型,这个是C++编译器要求的,如果要自己重载operator new函数,返回值必须为void* 类型,第一个参数必须为size_t类型,否则,编译器会返回如下错误信息:
error: ‘operator new’ takes type ‘size_t’ (‘unsigned int’) as first parameter
这里需要注意的一点是,我们可以利用operator new function可以重载的特点,可以通过参数传入一些额外的信息,来调试程序,检测内存泄露等。比如,我们可以像下面这样重载,传入调用处的行号,函数名,这样就可以跟踪内存的分配使用情况:
void * operator new(size_t unSize, int nLine, const char * pFunc)
{
prinft("Line: %d, Func: %s, allocate %u byte(s)\n", nLine, pFunc, unSize);
return malloc(unSize);
}
(2) operator delete function
void operator delete( void * );
void operator delete( void *, size_t );
上面是operator delete function的原型。operator delete function也有全局的和类成员的两种。这里需要注意,一个类只能有一个operator delete function做为其成员函数,而且必须为上面两种中的其中一种,没有其它的形式。如果一个类实现了自己的operator delete function成员函数,那么在释放该类型的内存时,编译器便会调用成员operator delete function, 而不是全局的。
上面的两种原型,第一种,在调用的时候,编译器会把要释放的内存的首地址传入,第二种,在调用的时候,编译器会把要释放的内存的首地址和大小都传入。因此,可以利用这一特性,如果我们在基类中实现第二种形式的operator delete function的成员函数,那么便可以用之来释放子类类型的内存(具体参考最后面的例子)。
2. 运算符(Operator)
(1) new operator
[::] new [placement] new-type-name [new-initializer]
[::] new [placement] ( type-name ) [new-initializer]
注:上面的'[]’表示在其中的部分是optional(可选的)
上面是new operator的原型。在C++中,动态内存的分配,通常都是调用new operator来完成的,利用new operator来分配动态内存,编译器要做下面两项工作:
- a. 调用operator new function分配内存(allocate the memory)
- b. 调用构造函数(call the constructor)来进行初始化
下面来说一说new operator的原型中各部分到底是干什么的:
placement: 如果你重载了operator new function, placement可以用来传递额外的参数
type-name: 指定要分配的内存的类型,可以是内建(built-in)类型,也可以是用户自定义类型
new-initializer: 指定对分配后内存的初始化的参数,也就的构造函数的参数 。这里需要注意一点,在分配一个对象的数组类型的内存时,不能够指定初始化参数;换言之,要想分配一个对象的数组类型的内存,该对象必须有缺省构造函数
(2) delete opeartor
[::] delete cast-expression
[::] delete [ ] cast-expression
上面是delete operator的原型,第一种用来释放普通的对象(包括内建类型)类型的内存,第二种用来释放对象的数组类型的内存。在C++中,用new operator分配的动态内存,必须调用delete operator来释放,通常用delete operator释放内存编译器要做下面两项工作:
- a. 调用对象析构函数来析构对象
- b. 调用operator delete function来释放内存(deallocate the memory)
3. 关于new/delete使用过程中一些需要注意的点
(1)如何区别operator new/delete function 与 new/delete operator ?
通过上面的讲述,不难看出,我们分配/释放动态内存,调用的是new/delete operator, 而在调用new/delete的过程中,编译器会自动调用operator new/delete function来完成实际的内存分配/释放的工作
(2) 用delete operator去释放一块不是由new operator释放的内存,结果是不可预料的,因此,切记,operator new与operator delete一定要配对使用,这是写好程序的基础
(3) new operator调用失败会抛出std::bad_alloc异常,前提是你没有自己重载对应的operator new function;delete operator失败,常见的原因是多次delete同一块内存
(4) 如果一块内存被delete后,再对它解引用(Dereference),结果也是不可预测的,很可能导致程序崩溃
(5) delete一个空(NULL)指针是安全的,没有任何害处的
(6) 类成员类型的operator new/delete函数必须为静态(static)函数,因此它们不能为虚函数(virtual function),也遵守public, protected, private的访问权限控制
4. 关于上面所讲的内容的一些例子:
#include <cstdio>
#include <cstdlib> void * operator new(size_t unSize)
{
printf("operator new called\n");
return malloc(unSize);
}
void * operator new[](size_t unSize)
{
printf("operator [] called\n");
return malloc(unSize);
} void * operator new(size_t unSize, int nLine, const char * pFunc)
{
printf("operator new called, line: %d, func: %s\n",
nLine, pFunc);
return malloc(unSize);
}
//注意:c++14才支持全局delete或delete【】有多个参数,参看参考资料2.下面两个delete不会覆盖全局的。
void operator delete(void * pMem,size_t unSize)
{
printf("delete1: %u\n", unSize);
free(pMem);
}
void operator delete[](void * pMem, size_t unSize)
{
printf("delete[]: %u\n", unSize);
free(pMem);
} class A
{
public: A(int a = ) :
_a(a)
{
printf("constructor called\n");
}
virtual ~A()
{
printf("~A()\n");
}
void * operator new(size_t unSize)
{
printf(" calledA\n");
return malloc(unSize); }
//注意:但是支持自定义类型操作符new或delete重载支持size_t参数。即要删除对象的大小delete,要删除对象数组大小delete[].。
void operator delete(void * pMem, size_t unSize)
{
printf("delete2: %u\n", unSize);
free(pMem);
}
void operator delete[](void * pMem, size_t unSize)
{
printf("delete[]: %u\n", unSize);
free(pMem);
} private: int _a;
}; class B: public A
{
public:
//隐式的为静态函数。
void * operator new(size_t unSize, int nLine, const char * pFunc)
{
printf("operator new called, line: %d, fileB: %s\n",
nLine, pFunc);
printf("operator new: %u\n", unSize);
//_b=0;
return malloc(unSize);
} ~B()
{
printf("~B()\n");
} int _b; int _bb;
}; int main()
{
A * pA = new A();
printf("#######\n"); A * pB = new (__LINE__, __FILE__) B();
printf("#######\n"); A * szA = new A[];
B *szB = new B[];
printf("#######\n"); delete pA;
printf("#######\n"); delete pB;
printf("#######\n"); delete [] szA;
printf("#######\n"); delete [] szB;
printf("#######\n"); //下面两个不是自定义类,没有类重载new.delete故只能调用全局的,本程序全局不支持size_t参数,故只能调用标准C++中的全局operate delete.故不会打印信息。
char * pC = new char[];
delete [] pC; char *pu = NULL;
delete pu;
}
clang运行结果如下: calledA
constructor called
#######
operator new called,
operator new:
constructor called
#######
operator [] called
constructor called
constructor called
constructor called
constructor called
constructor called
constructor called
constructor called
constructor called
constructor called
constructor called
operator [] called
constructor called
constructor called
constructor called
constructor called
constructor called
constructor called
constructor called
constructor called
constructor called
constructor called
#######
~A()
delete2:
#######
~B()
~A()
delete2:
#######
~A()
~A()
~A()
~A()
~A()
~A()
~A()
~A()
~A()
~A()
delete[]: //注意84=4+8*10,数组分配的堆空间,第一个int空间放数组个数,接下来顺序放对象
#######
~B()
~A()
~B()
~A()
~B()
~A()
~B()
~A()
~B()
~A()
~B()
~A()
~B()
~A()
~B()
~A()
~B()
~A()
~B()
~A()
delete[]:
#######
operator [] called //调用覆盖的全局函数operate new [],打印信息。
//delete[]调用全局的。
new与delete(new[]与delete[])的调用顺序:类中的重载,本文件中的重载,c++源码的原始操作符函数。
详解new/delete(整合)的更多相关文章
- 详解Oracle DELETE和TRUNCATE 的区别(摘)
语法delete from aa truncate table aa 区别 1.delete from后面可以写条件,truncate不可以. 2.delete from记录是一条条删的,所删除的每行 ...
- java实现rabbitMQ延时队列详解以及spring-rabbit整合教程
在实际的业务中我们会遇见生产者产生的消息,不立即消费,而是延时一段时间在消费.RabbitMQ本身没有直接支持延迟队列功能,但是我们可以根据其特性Per-Queue Message TTL和 Dead ...
- 详解Oracle DELETE和TRUNCATE 的区别
原文地址:http://www.cnblogs.com/simplefrog/archive/2012/07/30/2615169.html 语法delete from aa truncate tab ...
- idea spring+springmvc+mybatis环境配置整合详解
idea spring+springmvc+mybatis环境配置整合详解 1.配置整合前所需准备的环境: 1.1:jdk1.8 1.2:idea2017.1.5 1.3:Maven 3.5.2 2. ...
- MySQL权限详解
MySQL权限级别介绍 MySQL权限级别 全局性的管理权限,作用于整个MySQL实例级别 数据库级别的权限,作用于某个指定的数据库上或者所有的数据库上 数据库对象级别的权限,作用于指定的数据库对象上 ...
- 企业级Apache详解
安装Apache #Apache安装 rpm -qa|grep httpd yum install httpd #2编译安装: -->推荐安装 cd /root/software yum -y ...
- 转载 Spring、Spring MVC、MyBatis整合文件配置详解
Spring.Spring MVC.MyBatis整合文件配置详解 使用SSM框架做了几个小项目了,感觉还不错是时候总结一下了.先总结一下SSM整合的文件配置.其实具体的用法最好还是看官方文档. ...
- 如约而至,Java 10 正式发布! Spring+SpringMVC+MyBatis+easyUI整合进阶篇(十四)Redis缓存正确的使用姿势 努力的孩子运气不会太差,跌宕的人生定当更加精彩 优先队列详解(转载)
如约而至,Java 10 正式发布! 3 月 20 日,Oracle 宣布 Java 10 正式发布. 官方已提供下载:http://www.oracle.com/technetwork/java ...
- (转) shiro权限框架详解06-shiro与web项目整合(上)
http://blog.csdn.net/facekbook/article/details/54947730 shiro和web项目整合,实现类似真实项目的应用 本文中使用的项目架构是springM ...
随机推荐
- web中使用扫描枪
扫描枪实际上就是一输入设备,只不过它每次在输入的内容后面添加一个回车.因此在web中可以使用js监听回车事件.处理代码如下: jQuery(function() { jQuery(doc ...
- Expect:100-Continue & HTTP 417 Expectation[转]
Expect:100-Continue & HTTP 417 Expectation 背景:今天调试火车票查询的代码,发现一个奇怪的事情,如果使用公司本地的代理,那么一切正常,如果使用的是公司 ...
- POJ 2774 Long Long Message&&HDU 1403 Longest Common Substring&&COJ 1203
后缀数组的买1送2题... HDU的那题数据实在是太水了,后来才发现在COJ和POJ上都是WA..原因在一点:在建立sa数组的时候里面的n应该是字符串长度+1....不懂可以去看罗大神的论文... 就 ...
- Oracle Bills of Material and Engineering Application Program Interface (APIs)
In this Document Goal Solution 1. Sample Notes for BOM APIs 2. Datatypes used in these APIs ...
- C# in Depth阅读笔记3:C#3特性
1.隐式类型 var a=new Dictionary<string ,list<person>>();可以简化代码,增强可读性. 2.简化初始化 (1)设置简单属性 Pers ...
- 关于我和document.write那点不得不说的事
一直用document.write()方法向浏览器中显示数据用,把它当做Alert()使用, 看来这样用有些大材小用了,下面说说它的主要用处. document.write()方法可以用在两个方面: ...
- MJExtension
MJExtension 长话短说下面我们通过一个列子来看下怎么使用 1. 先把框架拉进去你的项目 2. 首先我这次用到的json最外层是一个字典,根据数据的模型我们可以把这个归类为字典中有数组,数组中 ...
- C# - 动态连接数据库字符串
String conStr = @"Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|数据库文件.mdf;Integrated ...
- google base 之MessagePumpForUI
base库中比较有意思就是这个类了,如同很多界面库一样,创建了一个隐藏窗口来处理需要在界面线程处理的消息,大体原理也就是需要执行task的时候发送一个自定义的消息,当窗口接收到task的时候调用保存起 ...
- MAC上使用maven打android的包,报错:No Android SDK path could be found. 解决办法
对android工程运行mvn compile出现如下信息: No Android SDK path could be found. You may configure it in the pom u ...