一、new的浅析

在C++中,new主要由三种形式:new operator、operator new和placement new


• new operator

new operator即一些C++书籍中(如《C++ Primer》)所说的new表达式(new expression) ,也是我们在C++中用来进行动态内存空间开辟的主要工具。

语法:

  1. 语法1:类型* 指针名=new 类型 //在内存的堆区动态开辟一个变量大小的空间
  2. 语法2:类型* 指针名=new 类型(初始化值) //在内存的堆区动态开辟一个变量大小的空间,并对该动态变量进行初始化
  3. 语法3:类型*指针名=new 类型[数组大小] //在内存的堆区动态开辟一个数组大小的空间

示例:

  1. int *value=new int; //语法1示例
  2. string *name=new string("Tomwenxing"); //语法2示例
  3. double *array=new int[]; //语法3示例

事实上,当我们使用new来开在内存中动态开辟空间时,new operator主要完成了三项工作:

1.在内存的堆区中动态开辟空间(由operator new来完成,很多编译其借助C语言中的malloc来实现)

2.调用构造函数(C语言中的malloc只开辟空间而不调用构造函数,这也是为什么new可以进行初始化而malloc不行的原因)

  1. #include<iostream>
  2. #include<string>
  3. using namespace std;
  4. class Student{
  5. public:
  6. Student(){
  7. cout<<"调用默认构造函数"<<endl;
  8. }
  9. Student(string name):Name(name){
  10. cout<<"调用带参数的构造函数"<<endl;
  11. }
  12. private:
  13. string Name;
  14. };
  15.  
  16. int main(){
  17. Student *stu1=new Student;
  18. Student *stu2=new Student("Tomwenxing");
  19. delete stu1;
  20. delete stu2;
  21. return ;
  22. }

3.返回分配的指针(C语言的malloc只返回void*指针,而new返回的指针都有特定的类型而非空指针)

特别注意:new operator=operator new+Constructor(构造函数)


 • operator new

new operator的第一步在内存的堆区动态开辟内存实际上就是通过operator new来完成的。通常情况下,new operator的声明如下:

  1. void* operator new(size_t size);

特别注意:

1.operator new的返回值类型是void* ,表示operator new的返回值是一个指向一块原始的未设置初始值的内存。换句话说,operator new的唯一任务就是负责内存分配;而获取operator new返回的内存并将之转换为一个对象则是new operator的责任。

2.operator new中的size_t参数表示的是需要分配的内存的大小;我们可以将operator new进行重载,甚至添加额外的参数,但operator new的第一个参数的类型必须总是size_t  [注]:operator new的size_t参数的大小一般由系统根据实际类型调用sizeof计算得来

  1. #include<iostream>
  2. #include<string>
  3. using namespace std;
  4. class Student{
  5. public:
  6. Student(){
  7. cout<<"调用默认构造函数"<<endl;
  8. }
  9. Student(string name):Name(name){
  10. cout<<"调用带参数的构造函数"<<endl;
  11. }
  12. 12 void* operator new(size_t size){ //对operator new进行了重载
  13. 13 cout<<"调用了operator new"<<endl;
  14. 14 return malloc(size);
  15. 15 }
  16. private:
  17. string Name;
  18. };
  19.  
  20. int main(){
  21. Student *stu1=new Student;
  22. cout<<"---------分界线-----------------"<<endl;
  23. Student *stu2=new Student("Tomwenxing");
  24. delete stu1;
  25. delete stu2;
  26. return ;
  27. }

3.利用new创建动态数组时无法对数组中的元素显式初始化(也就是说只能调用默认构造函数,而不能调用带参数的构造函数),并且可以对new[]进行单独的重载。

  1. #include<iostream>
  2. #include<string>
  3. using namespace std;
  4. class Student{
  5. public:
  6. Student(){
  7. 7 cout<<"调用默认构造函数"<<endl;
  8. }
  9. Student(string name):Name(name){
  10. cout<<"调用带参数的构造函数"<<endl;
  11. }
  12. void* operator new(size_t size){ //重载operator new
  13. cout<<"调用了operator new"<<endl;
  14. return malloc(size);
  15. }
  16. 16 void* operator new[](size_t size){ //重载operator new[]
  17. 17 cout<<"调用operator new[]"<<endl;
  18. 18 return malloc(size);
  19. 19 }
  20. private:
  21. string Name;
  22. };
  23.  
  24. int main(){
  25. Student *stu=new Student[];
  26. delete []stu;
  27. return ;
  28. }

4.可以像调用其他普通函数一样,手动的调用operator new,例如:

  1. void* name=operator new(sizeof(string));

这里的operator new将返回指针,指向一块足够容纳一个string对像的内存


• placement new

从本质上来讲,placement new是对operator new的一个重载,它的声明如下:

  1. void* operator new(size_t size,void* ptr);

特别注意:

1.与operator new不同,placement new定义在头文件"new.h"中,并且相比与operator new多接受一个void* 型指针参数ptr,但它也只是简单地将指针ptr返回。

placement new在头文件new.h中的源代码:

2.placement new可以在其参数指针ptr所指地址上构建一个对象(通过调用其构造函数)从而实现定位构造,也就是说placement new可以在取得一块可以容纳指定类型的对象的内存后,在这块内存上手动构造该类型的对象。而new operator的第二步(调用构造函数)通常就是通过placement new来实现的

3.placement new的调用语法:

  1. 语法1new(指针) 类() //调用默认构造函数
  2. 语法2new(指针) 类(参数) //调用带参数的构造函数

示例:

  1. #include<iostream>
  2. #include<string>
  3. #include<new>
  4. using namespace std;
  5. class Student{
  6. public:
  7. Student(){
  8. cout<<"调用默认构造函数"<<endl;
  9. }
  10. Student(string name):Name(name){
  11. cout<<"调用带参数的构造函数"<<endl;
  12. }
  13. private:
  14. string Name;
  15. };
  16.  
  17. void* operator new(size_t size){ //重载operator new
  18. cout<<"调用了operator new"<<endl;
  19. return malloc(size);
  20. }
  21. int main(){
  22. //Student *stu1=new Studdent;
  23. 23 Student *stu1=(Student*)::operator new(sizeof(Student));
  24. 24 new(stu1) Student();
  25. delete stu1;
  26. cout<<"---------分界线--------------"<<endl;
  27. //Student *stu2=new Student("Tomwenxing");
  28. 28 Student *stu2=(Student*)::operator new(sizeof(Student));
  29. 29 new(stu2) Student("Tomwenxing");
  30. delete stu2;
  31. return ;
  32. }

4.placement new虽然实现了在指定内存地址上用指定类型的构造函数来构造一个对象的功能,但除非特别必要,不要直接使用placement new,因为这毕竟不是用来构造对象的正式写法,而只不过是new operator的一个步骤而已,当我们使用new operator时,编译器会相应地自动生成调用placement new的代码,而不需要我们手动对其进行编写。


二、delete的浅析

在C++中,delete的使用和new的使用是相对应


• delete operator

delete operator,又称delete表达式(delete expression) ,它和new operator相对应,主要功能是用来释放动态开辟的内存空间的

语法:

  1. 语法1delete 指针名 //释放指针所指的动态开辟的内存空间(存变量)
  2. 语法2delete []指针名 //释放指针所指的动态开辟的内存空间(存数组)

示例:

  1. string *name=new string("Tomwenxing");
  2. delete name;//语法1的演示
  3.  
  4. int *data=new int[];
  5. delete []data;//语法2的演示

事实上和new operator相似,delete operator在释放动态开辟的内存空间时,主要完成了两项工作:

1.调用析构函数,释放动态空间中存储的内容

2.释放动态空间(由operator delete来完成,很多编译其借助C语言中的free来实现)

  1. #include<iostream>
  2. #include<string>
  3. #include<new>
  4. using namespace std;
  5. class Student{
  6. public:
  7. Student(){
  8. cout<<"调用默认构造函数"<<endl;
  9. }
  10. Student(string name){
  11. cout<<"调用带参数的构造函数"<<endl;
  12. Name=new string;
  13. *Name=name;
  14. }
  15. ~Student(){
  16. cout<<"调用析构函数"<<endl;
  17. delete Name;
  18. }
  19. private:
  20. string *Name;
  21. };
  22.  
  23. int main(){
  24. Student *stu=new Student("Tomwenxing");
  25. delete stu;
  26. return ;
  27. }

Question:delete operator为什么先调用析构函数然后才释放动态开辟的内存空间呢?

Answer:以上面的示例为例,当指向完语句Student *stu=new Student("Tomwenxing")之后,内存中的空间分配情况大致如下:

此时如果不先调用析构函数来释放构造函数中new operator开辟的内存空间,而是先释放main函数中new operator开辟的内存空间,则会导致内存泄漏(堆区中动态开辟的用来存储字符串“Tomwenxing”的内存空间无法释放,因为此时指向这个空间的指针Name所占据的内存空间已经被释放,系统将无法准确定位该内存空间)


• operator delete

delete operator的第二步(释放动态开辟的内存空间)是通过operator delete来实现的,它的声明通常如下:

  1. void operator delete(void *p);

特别注意:

1.operator delete的返回值类型是void而不是void*,并且operator delete和operator delete[]均支持重载。

2.如果我们只打算处理原始的、为设置初始值的内存,这应该完全回避new operator和delete operator,改调用operator new取得内存并以operator delete释放内存

  1. void *buffer=operator new(*sizeof(char));
  2. ......
  3. operator delete(buffer);

这组行为相当于在C++中调用malloc和free

3.很多编译器是通过free来实现operator delete的

C++:new&delete的更多相关文章

  1. SQL-W3School-基础:SQL DELETE 语句

    ylbtech-SQL-W3School-基础:SQL DELETE 语句 1.返回顶部 1. DELETE 语句 DELETE 语句用于删除表中的行. 语法 DELETE FROM 表名称 WHER ...

  2. Git异常:Cannot delete the branch 'test1' which you are currently on

    GitHub实战系列汇总:http://www.cnblogs.com/dunitian/p/5038719.html ———————————————————————————————————————— ...

  3. SQL SERVER 2005删除维护作业报错:The DELETE statement conflicted with the REFERENCE constraint "FK_subplan_job_id"

    案例环境: 数据库版本: Microsoft SQL Server 2005 (Microsoft SQL Server 2005 - 9.00.5000.00 (X64) ) 案例介绍: 对一个数据 ...

  4. (转)iOS sqlite :truncate/delete/drop区分

    转自:http://blog.sina.com.cn/s/blog_6755689f0101fofb.html 相同点: 1.truncate和不带where子句的delete.以及drop都会删除表 ...

  5. Hibernate级联删除时:Cannot delete or update a parent row: a foreign key constraint fails异常

    在删除主表数据时,报了一个异常 Cannot delete or update a parent row: a foreign key constraint fails 原因是主表中还包含字表的数据, ...

  6. 微信SDK导入报错 Undefined symbols for architecture i386:"operator delete[](void*)", referenced from:

    异常信息: Undefined symbols for architecture i386:  "operator delete[](void*)", referenced fro ...

  7. 转载:指针delete后要设置为NULL

    本文来自:http://rpy000.blog.163.com/blog/static/196109536201292615547939/ 众所周知,最开始我们用new来创建一个指针,那么等我们用完它 ...

  8. C++基础学习7:new/delete操作符

    在C语言中,动态分配和释放内存的函数是malloc.calloc和free,而在C++语言中,new.new[].delete和delete[]操作符通常会被用来动态地分配内存和释放内存. 需要注意的 ...

  9. 创建包含CRUD操作的Web API接口5:实现Delete方法

    本节是前面四节的延续,在前面几节中我们创建了Web API并添加了必要的基础设施,实现了Get.Post.和Put方法.本节中,我们将介绍如何在Web API中实现Delete方法. 在RESTful ...

随机推荐

  1. Apache Shiro(一)-登录认证和权限管理初识

    What is Apache Shiro? Apache Shiro是一个功能强大.灵活的,开源的安全框架.它可以干净利落地处理身份验证.授权.企业会话管理和加密. Apache Shiro的首要目标 ...

  2. 使用XWAF框架(2)——上传文件

    XWAF提供了上传文件的HttpFileUploader工具类,具备强大的多文件上传.文件类型过滤.文件大小限制.存储目录设置.文件名称更改等功能,简化了Web应用开发的编程工作. 它能同时解析表单参 ...

  3. 能够让你装逼的10个Python小技巧

      列表推导式 你有一个list: bag = [1, 2, 3, 4, 5] 现在你想让所有元素翻倍,让它看起来是这个样子: [2, 4, 6, 8, 10] 大多初学者,根据之前语言的经验会大概这 ...

  4. JS实现图片的淡入和淡出的两种方法,如有不足,还请前辈多多指导^-^~

    今天下午练习了下这个图片的淡入淡出小demo,如有不足,还请前辈多多指导^-^~ 总结如下: 第一种方法: 个人觉得第一种方法比较好,同时兼容IE8以下浏览器,但是如下代码中,不知可不可以将timer ...

  5. NRF52832初步使用

    开发环境搭建 开发环境涉及到协议栈SDK版本.keil PACK版本的匹配问题,目前测试通过的环境如下: windows系统:win10 硬件:NRF52832测试板.JLINK-V8仿真器 Keil ...

  6. iOS Swift WisdomKeyboardKing 键盘智能管家SDK

    iOS Swift WisdomKeyboardKing 键盘智能管家SDK [1]前言:    今天给大家推荐个好用的开源框架:WisdomKeyboardKing,方面iOS日常开发,优点和功能请 ...

  7. 5469: [FJOI2018]领导集团问题

    5469: [FJOI2018]领导集团问题 链接 题意: 要求在一棵树内选一个子集,满足子集内的任意两个点u,v,如果u是v的祖先,那么u的权值小于等于v. 分析: dp[u][i]表示在u的子树内 ...

  8. java日志框架log4j详细配置及与slf4j使用教程

    一.log4j基本用法 首先,配置log4j的jar,maven工程配置以下依赖,非maven工程从maven仓库下载jar添加到“build path” 1 2 3 4 5 <dependen ...

  9. mysql索引原理及查询速度优化

    一 介绍 为何要有索引? 一般的应用系统,读写比例在10:1左右,而且插入操作和一般的更新操作很少出现性能问题,在生产环境中,我们遇到最多的,也是最容易出问题的,还是一些复杂的查询操作,因此对查询语句 ...

  10. 取消Ubuntu18.04开机输入密码登录

    设置>>详细信息>>用户>>解锁>>(输入密码进行认证)>>{自动登录}选项打开