一、new的浅析

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


• new operator

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

语法:

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

示例:

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

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

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

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

 #include<iostream>
#include<string>
using namespace std;
class Student{
public:
Student(){
cout<<"调用默认构造函数"<<endl;
}
Student(string name):Name(name){
cout<<"调用带参数的构造函数"<<endl;
}
private:
string Name;
}; int main(){
Student *stu1=new Student;
Student *stu2=new Student("Tomwenxing");
delete stu1;
delete stu2;
return ;
}

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

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


 • operator new

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

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计算得来

 #include<iostream>
#include<string>
using namespace std;
class Student{
public:
Student(){
cout<<"调用默认构造函数"<<endl;
}
Student(string name):Name(name){
cout<<"调用带参数的构造函数"<<endl;
}
12 void* operator new(size_t size){ //对operator new进行了重载
13 cout<<"调用了operator new"<<endl;
14 return malloc(size);
15 }
private:
string Name;
}; int main(){
Student *stu1=new Student;
cout<<"---------分界线-----------------"<<endl;
Student *stu2=new Student("Tomwenxing");
delete stu1;
delete stu2;
return ;
}

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

 #include<iostream>
#include<string>
using namespace std;
class Student{
public:
Student(){
7 cout<<"调用默认构造函数"<<endl;
}
Student(string name):Name(name){
cout<<"调用带参数的构造函数"<<endl;
}
void* operator new(size_t size){ //重载operator new
cout<<"调用了operator new"<<endl;
return malloc(size);
}
16 void* operator new[](size_t size){ //重载operator new[]
17 cout<<"调用operator new[]"<<endl;
18 return malloc(size);
19 }
private:
string Name;
}; int main(){
Student *stu=new Student[];
delete []stu;
return ;
}

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

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

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


• placement new

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

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:new(指针) 类() //调用默认构造函数
语法2:new(指针) 类(参数) //调用带参数的构造函数

示例:

 #include<iostream>
#include<string>
#include<new>
using namespace std;
class Student{
public:
Student(){
cout<<"调用默认构造函数"<<endl;
}
Student(string name):Name(name){
cout<<"调用带参数的构造函数"<<endl;
}
private:
string Name;
}; void* operator new(size_t size){ //重载operator new
cout<<"调用了operator new"<<endl;
return malloc(size);
}
int main(){
//Student *stu1=new Studdent;
23 Student *stu1=(Student*)::operator new(sizeof(Student));
24 new(stu1) Student();
delete stu1;
cout<<"---------分界线--------------"<<endl;
//Student *stu2=new Student("Tomwenxing");
28 Student *stu2=(Student*)::operator new(sizeof(Student));
29 new(stu2) Student("Tomwenxing");
delete stu2;
return ;
}

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:delete 指针名 //释放指针所指的动态开辟的内存空间(存变量)
语法2:delete []指针名 //释放指针所指的动态开辟的内存空间(存数组)

示例:

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

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

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

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

 #include<iostream>
#include<string>
#include<new>
using namespace std;
class Student{
public:
Student(){
cout<<"调用默认构造函数"<<endl;
}
Student(string name){
cout<<"调用带参数的构造函数"<<endl;
Name=new string;
*Name=name;
}
~Student(){
cout<<"调用析构函数"<<endl;
delete Name;
}
private:
string *Name;
}; int main(){
Student *stu=new Student("Tomwenxing");
delete stu;
return ;
}

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

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

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


• operator delete

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

void operator delete(void *p);

特别注意:

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

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

 void *buffer=operator new(*sizeof(char));
......
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. 第11章 GPIO输出-使用固件库点亮LED—零死角玩转STM32-F429系列

    第11章     GPIO输出—使用固件库点亮LED 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku. ...

  2. 【ps】Photoshop

    Photoshop Cs6  存在百度云上 另外,想用切图插件cutterman,则必须安装相匹配的ps版本

  3. K8S学习心得 == 安装虚拟路由器RouterOS

    使用RouterOS, 搭建虚拟路由器,并且配置多个网关互通.配置步骤如下.   基础配置 1. RouterOS 服务器,设置如下             2. VM 不同网段的设置 == 192. ...

  4. ruby的循环控制命令loop等

    ruby的循环有以下几种: times方法 for while until(与while相反) each(与for极度相似,在ruby内部,for语句是用each实现的) loop(无限循环,与bre ...

  5. AWVS使用手册

    目录: 0×00.什么是Acunetix Web Vulnarability Scanner ( What is AWVS?) 0×01.AWVS安装过程.主要文件介绍.界面简介.主要操作区域简介(I ...

  6. QT4.8.6之qt.network.ssl: QSslSocket: cannot call unresolved function ERR_get_error

    想试着用qt写一个爬虫,编译的时候报如下错误 qt.network.ssl: QSslSocket: cannot call unresolved function ERR_get_error qt. ...

  7. Python day1 ---python基础1.1

    1.模块初识2..pyc是个什么鬼?3.数据类型初识 4.数据运算5.入门知识拾遗 1.模块初识 sys模块 import sys print(sys.path) #打印环境变量 'D:\\Pycha ...

  8. gabor变换人脸识别的python实现,att_faces数据集平均识别率99%

    大家都说gabor做人脸识别是传统方法中效果最好的,这几天就折腾实现了下,网上的python实现实在太少,github上的某个版本还误导了我好几天,后来采用将C++代码封装成dll供python调用的 ...

  9. 给浏览器和各种软件配置 http https socks5 代理 proxy

    只需要像在 idea 里一样,配置好sr的本地代理ip:127.0.0.1   本地代理端口:1080 浏览器和软件的流量即可走 sr ,就能被 sr 代理了 用上代理之后,确实快了好多,这里是在打开 ...

  10. [BZOJ4475][JSOI2015]子集选取[推导]

    题意 题目链接 分析 显然可以看成一个位数为 \(n\) 的二进制数然后每一位分开考虑然后求和.最后的答案是 \(w^n\) 的形式. 考虑一个dp. 定义状态 \(f_{i}\) 表示选择了长度为 ...