• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >C语言 > C++动态内存分配(new/new[]和delete/delete[])详解

C++动态内存分配(new/new[]和delete/delete[])详解

作者: 字体:[增加 减小] 来源:互联网 时间:2017-05-28

通过本文主要向大家介绍了c++ new delete,c++中new和delete,c++ delete,c++delete函数,c++ new等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

C++动态内存分配(new/new[]和delete/delete[])详解

为了解决这个普通的编程问题,在运行时能创建和销毁对象是基本的要求。当然,C已提供了动态内存分配函数malloc( )和free( ),以及malloc( )的变种(realloc:改变分配内存的大小,calloc:指针指向内存前初始化),这些函数在运行时从堆中(也称自由内存)分配存储单元,但是运用这些库函数需要计算需要开辟内存的大小,容易出现错误。

     那么通常我们在C语言中我们开辟内存的方式如下:

(void*)malloc(sizeof(void)); 
</div>

然而,在C+ +中这些函数不能很好地运行。构造函数不允许通过向对象传递内存地址来初始化它。如果那么做了,我们可能

  • 忘记了。则对象初始化在C + +中难以保证。
  • 期望某事发生,但结果是在给对象初始化之前意外地对对象作了某种改变。
  • 把错误规模的对象传递给了它。

当然,即使我们把每件事都做得很正确,修改我们的程序的人也容易犯同样的错误。不正确的初始化是编程出错的主要原因,所以在堆上创建对象时,确保构造函数调用是特别重要的。

C+ +是如何保证正确的初始化和清理并允许我们在堆上动态创建对象的呢?

    答案是使动态对象创建成为语言的核心。malloc( )和free( )是库函数,因此不在编译器控制范围之内。如果我们有一个能完成动态内存分配及初始化工作的运算符和另一个能完成清理及释放内存工作的运算符,编译器就可以保证所有对象的构造函数和析构函数都会被调用。

    若使用原始的动态内存开辟方式就会显得很繁琐,具体代码如下:

#include<cstdlib> 
#include<cstring> 
#include<iostream> 
using namespace std; 
class Obj 
{ 
 int i,j,k; 
 enum {sz=100}; 
 char buf[sz]; 
public: 
  void initialize() 
  { 
    cout<<"initialize"<<endl; 
    i=k=j=0; 
    memset(buf,0,sz); 
  } 
  void destroy() const 
  { 
   cout<<"destroying Obj"<<endl; 
  } 
}; 
int main() 
{ 
  Obj* obj=(Obj*)malloc(sizeof(Obj)); 
   if(obj!=0) 
  obj->initialize(); 
  obj->destroy(); 
  free(obj); 
 return 0; 
}
</div>

     在上面这行代码中,我们可以看到使用malloc( )为对象分配内存:obj* Obj = (obj*)malloc(sizeof(obj)) ;
这里用户必须决定对象的长度(这也是程序出错原因之一)。因为它是一块内存而不是一个对象,所以malloc( )返回一个void*.C++不允许将一个void* 赋予任何指针,所以必须映射。因为malloc( )可能找不到可分配的内存(在这种情况下它返回 0),所以必须检查返回的指针以确信内存分配成功。

但最坏的是:Obj->initialize( ) ;用户在使用对象之前必须记住对它初始化。注意构造函数没有被使用,因为构造函数不能被显式地调用—而是当对象创建时由编译器调用。这里的问题是现在用户可能在使用对象时忘记执行初始化,因此这也是引入程序缺陷的主要来源。许多程序设计者发现 C的动态内存分配函数太复杂,令人混淆。所以, C程序设计者常常在静态内存区域使用虚拟内存机制分配很大的变量数组以避免使用动态内存分配。因为C++能让一般的程序员安全使用库函数而不费力,所以应当避免使用 C的动态内存方法。C++中的解决方案是把创建一个对象所需的所有动作都结合在一个称为new的运算符里。当用new(new的表达式)创建一个对象时,它就在堆里为对象分配内存并为这块内存调用构造函数。

    因此,如果我们写出下面的表达式foo *fp = new foo(1,2) ; 在运行时等价于调用malloc(sizeof(foo)),并使用(1,2)作为参数表来为

foo调用构造函数,返回值作为this指针的结果地址。在该指针被赋给 fp之前,它是不定的、未初始化的对象— 在这之前我们甚至不能触及它。它自动地被赋予正确的 foo类型,所以不必进行映射。缺省的new还检查以确信在传递地址给构造函数之前内存分配是成功的,所以我们不必显式地确定调用是否成功。在本章后面,我们将会发现,如果没有可供分配的内存会发生什么事情。我们可以为类使用任何可用的构造函数而写一个 ne w表达式。如果构造函数没有参数,可以写没有构造函数参数表的new表达式:

foo *fp = new foo ;我们已经注意到了,在堆里创建对象的过程变得简单了—只是一个简单的表达式 ,它带有内置的长度计算、类型转换和安全检查。这样在堆里创建一个对象和在栈里创建一个对象一样容易。

new表达式的反面是delete表达式。delete表达式首先调用析构函数,然后释放内存(经常是调用free( ))。正如new表达式返回一个指向对象的指针一样,delete表达式需要一个对象的地址。delete fp ;上面的表达式清除了早先创建的动态分配的对象foo。delete只用于删除由new创建的对象。如果用malloc( )(或calloc( )或realloc( ))创建一个对象,然后用delete删除它,这个行为是未定义的。因为大多数缺省的new和delete实现机制都使

用了malloc( )和free( ),所以我们很可能会没有调用析构函数就释放了内存。如果正在删除的对象指针是 0,将不发生任何事情。为此,建议在删除指针后立即把指针赋值为0以免对它删除两次。对一个对象删除两次一定不是一件好事,这会引起问题。

当创建一个new表达式时有两件事发生。首先,使用运算符new分配内存,然后调用构造函数。在delete表达式里,调用析构函数,然后使用运算符 delete释放内存。我们永远无法控制构造函数和析构函数的调用(否则我们可能意外地搅乱它们),但可以改变内存分配函数运算  符new和delete。被new和delete使用的内存分配系统是为通用目的而设计的。但在特殊的情形下,它不能满足我们的需要。改变分配系统的原因是考虑效率:我们也许要创建和销毁一个特定的类的非常多的对象以至于这个运算变成了速度的瓶颈。 C++允许重载new和delete来实现我们自己的存储分配方案,所以可以像这样处理问题。   

  另外一个问题是堆碎片:分配不同大小的内存可能造成在堆上产生很多碎片,以至于很快用完内存。也就是内存可能还有,但由于是碎片,找不到足够大的内存满足我们的需要。通过为特定类创建我们自己的内存分配器,可以确保这种情况不会发生。 
在嵌入和实时系统里,程序可能必须在有限的资源情况下运行很长时间。这样的系统也可能要求分配内存花费相同的时间且不允许出现堆内存耗尽或出现很多碎片的情况。由客户定制的内存分配器是一种解决办法,否则程序设计者在这种情况下要避免使用new和delete,从而失去了C + +很有价值的优点。 

当重载运算符new和delete时,记住只改变原有的内存分配方法是很重要的。编译器将用new代替缺省的版本去分配内存,然后为那个内存调用构造函数。所以,虽然编译器遇到new 时会分配内存并调用构造函数,但当我们重载new时,可以改变的只是内存分配部分。(delete 也有相似的限制。)

当重载运算符new时,也可以替换它用完内存时的行为,所以必须在运算符new里决定做什么:返回0、写一个调用new - handler的循环、再试着分配或用一个 bad_alloc异常处理重载new和delete与重载任何其他运算符一样。但可以选择重载全局内存分配函数,或为特定的类使用特定的分配函数

    当全局版本的new和delete不能满足整个系统时,对其重载是很极端的方法。如果重载全局版本,那么缺省版本就完全不能被访问—甚至在这个重载定义里也不能调用它们。

重载的ne w 必须有一个size_t 参数。这个参数由编译器产生并传递给我们,它是要分配内存的对象的长度。必须返回一个指向等于这个长度(或大于这个长度,如果我们有这样做的原因)的对象的指针,或如果没有找到存储单元(在这种情况下,构造函数不被调用),返回一个0。然而如果找不到存储单元,不能仅仅返回0,还应该调用new-handler或进行异常处理,通知这里存在问题。

运算符new的返回值是一个void *,而不是指向任何特定类型的指针。它所做的是分配内存,而不是完成一个对象的建立—直到构造函数调用了才完成对象的创建,这是由编译器所确保的动作,不在我们的控制范围内。

运算符delete接受一个指向由运算符new分配的内存的void *。它是一个void *因为它是在调用析构函数后得到的指针。析构函数从存储单元里移去对象。运算符 delete的返回类型是void。

下面提供了一个如何重载全局new和delete的简单的例子:

#include <stdlib.h> 
 
void * operator new(size_t sz) 
{ 
  printf("operator new:%d bytes\n",sz); 
  void* m=malloc(sz); 
  if(!m) puts("out of memory"); 
  return 0; 
} 
void operator delete(void* m) 
{ 
puts("operator delete"); 
free(m); 
} 
class s 
{ 
  int i[100]; 
public: 
  s(){puts("s::s()");} 
  ~s(){puts("s::~s()");} 
}; 
int main() 
{ 
 puts("creating & destorying an int "); 
 int* p=new int(47); 
 delete p; 
 puts("creating & destorying an s"); 
 s* S=new s; 
 delete S; 
 puts



 
分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • C++动态内存分配(new/new[]和delete/delete[])详解
  • C++中的delete不会将操作数置0
  • C++表达式new与delete知识详解
  • C++中delete和delete[]的区别
  • C++基础入门教程(五):new和delete
  • C++之CNoTrackObject类和new delete操作符的重载实例
  • c++中new和delete操作符用法
  • c++中new的三种用法详细解析
  • 浅析c++中new和delete的用法
  • C++ new/delete相关知识点详细解析

相关文章

  • 2017-05-28stl容器set,map,vector之erase用法与返回值详细解析
  • 2017-05-28C++遍历文件夹下文件的方法
  • 2017-05-28C语言中的链接编写教程
  • 2017-05-28C/C++中可变参数的用法详细解析
  • 2017-05-28基于C++ cin、cin.get()、cin.getline()、getline()、gets()函数的使用详解
  • 2017-05-28浅谈使用Rapidxml 库遇到的问题和分析过程(分享)
  • 2017-05-28C++设计模式编程中Template Method模板方法模式的运用
  • 2017-05-28解析在main函数之前调用函数以及对设计的作用详解
  • 2017-05-28C++如何动态的生成对象详解
  • 2017-05-28c++ 巧开平方的实现代码

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • VC中删除类的两种操作方法
    • 在c和c++中实现函数回调
    • 深入Main函数中的参数argc,argv的使用详解
    • win32 api实现简单的消息窗口示例
    • C++编程中的命名空间基本知识讲解
    • C语言判断一个数是否是2的幂次方或4的幂次方
    • C语言中socket相关网络编程函数小结
    • C++中的RAII机制详解
    • C语言实现找出二叉树中某个值的所有路径的方法
    • c语言中malloc、realloc与calloc 的区别以及联系

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有