C++内存分配与释放

1. new 运算符 与 operator new
一条 new 表达式语句( new Type; )中的 new 是指 new 运算符.
operator new 是定义在 #include <new> 中声明的一系列全局函数, 其中部分全局函数可被重写, 或在自定义类型定义为成员函数, 这样该类或其子类将使用成员函数的版本进行内存分配.
new 和 operator 对应用程序至关重要, 一旦应用程序定义了全局版本的 operator new/delete, 应用程序就负担起了分配对态内存的职责, 必须保证这两个函数的完全正确.

2. new 与 delete 运算符
对 new Klass 表达式, 编译器执行3个过程:
(1) 调用 operator new 或 operator new[] 函数分配一块足够大的,原始,未命名的内存以存储该类型的对象或对象的数组.
编译器查找的顺序为:首先在类及其基类中查找,其次全局作用域内查找,如果没有找到使用标准库定义的版本.
(2) 调用相应的构造函数, 构造这些对象, 并为其传入初始值.
(3) 对象被分配了空间并构造完成后, 返回一个指向该对象或对象数组的指针.
delete p 执行相反的过程:
(1) 调用对象或对象数组的每一个元素调用析构函数.
如果析构函数为虚函数(顶层基类析构函数为虚函数则该类析构函数也为虚函数), 调用对象实际类型的析构函数.
如果为对象数组, 则从后往前调用析构函数.
(2) 调用 operator delete 或 operator delete[] 函数释放内存.
重写 new 与 delete 实际是重写了 operator new 和 operator delete 函数, 应用程序无法改变 new 和 delete 运算符的行为!
显示的调用 operator 版本的函数与使用 new 或 delete 表达式形式, 形式无多大区别, 但他们之间的差异惊人, 需要仔细甄别.
如果想把内存分配和对象构造分离开来, 可以使用 placement new 形式(或 allocator 类).
char buf[100]; // 分配内存
Object* p = new (buf) Object{1}; // 构造对象
p->~Object(); // 析构对象

3. 编译器定义的不同版本的 operator new/delete 函数的功能
void* operator new(size_t) bad_alloc;
void* operator new[](size_t) bad_alloc;
void operator delete(void*) noexcept;
void operator delete[](void*) noexcept;
void* operator new(size_t, nothrow_t) noexcept;
void* operator new[](size_t, nothrow_t) noexcept;
void operator delete(void*, nothrow_t) noexcept;
void operator delete[](void*, nothrow_t) noexcept;
void* operator new(size_t, void* p) noexcept { return p; }
void* operator new[](size_t, void* p) noexcept { return p; }
void operator delete(void*, void*) noexcept {}
void operator delete[](void*, void*) noexcept {}
(1) operator new 默认版本在分配内存失败时抛出 bad_alloc, 其余的函数不抛出异常.
(2)第(1)-(8)个函数可以重写.
(3)第(9)-(12)函数的全局版本不能被重新定义(类成员版本无此限制), 实际上它们什么也不干, operator new 也只是简单的返回传入的地址(注意, 不分配内存).
其中p指向的内存, 可以为任意地址, 包括栈上分配的内存, 只要其大小足够容纳对象. 这样应用程序就可以在预先分配的内存上构造对象.
常见的 new 表达式使用形式约有不同, 如: Object* p = new (buf) Object{1}; // 以 buf 指定的地址构造一个 Object 对象, 其构造函数参数为 1.
如果使用此方式, 构造了一个对象, 在内存销毁前, 需要手动调用对象的析构函数销毁对象, 如 p.~Object();
(4)重写的 operator new 函数必须返回 void*, 并且第一个参数必须为 size_t 类型, 且该参数不能有默认实参. 重载的版本可以提供其它额外的参数.
(5)为对象数组分配空间时使用 operator new[], 传入第一个参数为数组所有元素所需的空间.
(6) operator new 在内存不足时会调用 new_handler 函数, 并要求其释放一部分内存, 只有在 new_handler 为空时才会抛出异常.
可以调有标准库函数 set_new_handler 重新设置 new_handler 为自已定义的版本.
(7)operator new 的 nothrow_t 版本并不能保证 new 不抛出异常. 举个例子:
Object* p = new (nothrow) Object{}; 虽然指定了为 Object 对象分配内存时不抛出异常, 但 Object 在构造其成员对象的过程中, 如果内存不足, 仍然会抛出 bad_alloc.
(8)如果调用 operator new 时, 要求分配的内存大小为 0 字节, 也会返回一个合法的地址.
(9)delete 删除 nullptr 永远是安全的行为.
(10)重写 operator new 时也要重写对应的版本的 operator delete.

直接使用内存示例(仅用作演示函数功能, 实际项目中不要使用):
const size_t BUF_SZ = 100;
void* pbuf = operator new(BUF_SZ); // 分配100个字节的内存, 此时无对象, 当然也就不会调用构造函数
memset(pbuf, '1', BUF_SZ);
char* pc = static_cast<char*>(pbuf);
pc[BUF_SZ - 1] = '\0';
cout << "kao:" << pc << endl;
operator delete(pbuf); // 释放内存, 不能直接使用 delete pbuf;

4. 类成员函数 operator new/delete(数组版本同理,不赘述)
(1) 重写这些函数与普通的 operator 系列函数(如 operator <)意义完全不同, 需要区别对待.
(2) 类成员函数的 operator new/delete 必须为 static 函数, 并且可以不使用 static 声明.
(3) 和其它成员函数一样, 受访问权限限定符限制, 例如 operator new 函数在类定义中声明为 private, 则该类及其子类都不能使用 new 分配对象.
(4) 一旦一个类型中定义了一个 operator new 版本, 则需要同时实现其它 #include <new> 中声明的其它版本, 否则将不可以使用, 这和其它函数的重载类似.
(5) 自定义类型中重载的 operator new 函数, 可以添加自定义参数. 但第一个参数必须为 size_t 类型, 并且返回类型必须为 void*.
(6) 如果定义类型自已的 operator new 或 operator delete, 可以使用作用局运算符调用全局函数的版本. 如 ::new KlassA;
(7) 当定义 operator delete 或 operator delete [] 时, 第二个形参可以为size_t类型的参数, 以提供第1形参所指对象的字节数.
此形参用于删除继承体系中的对象, 如果其类对象中有一个虚函数, 其大小将为指针所指对象的动态类型的大小. (对单个对象调用仍使用 delete p;)

5. 使用 allocator 类分配内存.
allocator 类定义在头文件 memory 中, 使用 allocator 可将内存分配和构造过程分离开来.
allocator 类是一个模板类, 可以在头文件中看到其完整定义. 它分配的内存是原始的, 未构造的, 标准库模板类采了此方法分配内存.
主要的成员函数:
allocator<t> alloc; // 定义一个名为 alloc 的 allocator 对象, 它可以为类型为 T 的对象分配内存
T* allocate(size_t n); 分配一段原始,未构造的内存, 保存 n 个 T 类型的对象
void construct(T* p, Args&&... args); args 被用来传递给构造函数, 用来在 p 指向的内存中构造一个对象
void destroy(T* p); 析构一个对象, 即调用对对象 p 调用 T 的析构函数, p 必须是已构造的对象
void deallocate(T* p, size_t n); p 必须是 allocate 返回的地址, 且 n 为 allocate 分配时指定的大小. 调用此函数之前, 应用程序必须确保对其中的每一个已初始化了的对象都调用了 destroy 函数
未构造对象前使用对象, 其行为是未定义的. 当销毁一个对象后, 可以在不释放内存前重复使用该内存.

C++内存分配与释放的更多相关文章

  1. C语言中的内存分配与释放

    C语言中的内存分配与释放 对C语言一直都是抱着学习的态度,很多都不懂,今天突然被问道C语言的内存分配问题,说了一些自己知道的,但感觉回答的并不完善,所以才有这篇笔记,总结一下C语言中内存分配的主要内容 ...

  2. DLL函数中内存分配及释放的问题

    DLL函数中内存分配及释放的问题 最近一直在写DLL,遇到了一些比较难缠的问题,不过目前基本都解决了.主要是一些内存分配引起问题,既有大家经常遇到的现象也有特殊的 情况,这里总结一下,做为资料. 错误 ...

  3. 内存管理概述、内存分配与释放、地址映射机制(mm_struct, vm_area_struct)、malloc/free 的实现

    http://blog.csdn.net/pi9nc/article/details/23334659 注:本分类下文章大多整理自<深入分析linux内核源代码>一书,另有参考其他一些资料 ...

  4. Com组件的内存分配和释放,CredentialProvider SHStrDup 字符串拷贝问题

    一.简单介绍 熟悉CredentialProvider的同学应该知道,他为一个Com组件,于是,在这里的内存分配(字符串拷贝)的一系列操作就要依照con的标准来. 二.Com组件的内存分配和释放 CO ...

  5. (转)C++ STL中的vector的内存分配与释放

    C++ STL中的vector的内存分配与释放http://www.cnblogs.com/biyeymyhjob/archive/2012/09/12/2674004.html 1.vector的内 ...

  6. C++学习011-常用内存分配及释放函数

    C++用有多种方法来分配及释放内存,下面是一些经常使用的内存分配及释放函数 现在我还是一个技术小白,一般用到也指示 new+delete 和 malloc和free 其他的也是在学习中看到,下面的文字 ...

  7. C/C++动态二维数组的内存分配和释放

    C语言: 1 //二维数组动态数组分配和释放 //数组指针的内存分配和释放 //方法一 char (*a)[N];//指向数组的指针 a = (char (*)[N])malloc(sizeof(ch ...

  8. vector的内存分配与释放

    1. vector内存分配 <Effective STL>中"条款14":使用reserve来避免不必要的重新分配 关于STL容器,最神奇的事情之一是只要不超过它们的最 ...

  9. linx常用查看命令和内存分配及释放

    1.命令行 运行时间多久:uptime 查看时间日期: date:date  -s '2014-7-4 10:35:20' hwcloc 查看内存分配: top free:http://blog.cs ...

随机推荐

  1. zabbix_agentd-install.sh (脚本部署zabbix_agentd服务)

    原文发表于cu:2016-05-20 基于http://www.cnblogs.com/netonline/p/7406598.html(http://blog.chinaunix.net/uid-2 ...

  2. H2O Driverless AI

    H2O Driverless AI(H2O无驱动人工智能平台)是一个自动化的机器学习平台,它给你一个有着丰富经验的“数据科学家之盒”来完成你的算法. 使AI技术得到大规模应用 各地的企业都意识到人工智 ...

  3. Hands on Machine Learning with sklearn and TensorFlow —— 一个完整的机器学习项目(加州房地产)

    数据集地址:https://github.com/ageron/handson-ml/tree/master/datasets 先行知识准备:NumPy,Pandas,Matplotlib的模块使用 ...

  4. Druid Monitor小记

    继上篇DruidDataSource源码分析之后 , 公司又要求做一个Druid的数据库监控 , 以及spring监控 , 研究一小时 , 总结出了一点经验 , 特此贴出来分享一下 一 . 利用Dru ...

  5. 亚马逊CEO贝索斯致股东信:阐述公司未来计划

    亚马逊CEO 杰夫·贝索斯(Jeff Bezos)今天发布年度股东信, 详细描述了亚马逊的产品.服务和未来计划,当然,信中并没有任何的硬数据,比如说亚马逊Kindle的销量等等.但这封信也包括一些颇令 ...

  6. 第一次c++团队合作作业期间第一篇随笔

    分析了自己分到的任务,我的理解是这样的:首先要生成程序主框架,在主框架中进行地图的描绘.我应该是先进行地图的拼接,把建筑物和地面都拼接好.然后再在地图上显示出英雄和小兵.同时还要在主框架中分析了自己分 ...

  7. 事后诸葛亮--Alpha版本总结

    目录 设想和目标 计划 资源 变更管理 设计/实现 测试/发布 团队的角色,管理,合作 总结: 本小组和其他组的评分 分工和贡献分 全组讨论的照片 问题 第一组提问回答:爸爸饿了队 第二组提问回答:拖 ...

  8. 缓存-System.Web.Caching.Cache

    实现 Web 应用程序的缓存. 每个应用程序域创建一个此类的实例,只要应用程序域将保持活动状态,保持有效. 有关此类的实例的信息,请通过Cache的属性HttpContext对象或Cache属性的Pa ...

  9. spring mvc4 找不到静态文件js/css/html 404

    说明: http://localhost:8080 指向的目录是WEB-INF所在的目录,也就是说请求静态资源时都是从该根目录开始查找.建议将所有静态文件放到和WEB-INF同级的目录下. 以 htt ...

  10. Struts2拦截器配置和使用

    拦截器是Struts2最强大的特性之一,它是一种可以让用户在Action执行之前和Result执行之后进行一些功能处理的机制. 说到拦截器interceptor,就会想到过滤器filter: 过滤器f ...