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. 各web服务器的特点和优势

    1.Tomcat 和 Jetty 面向java语言 天生就是重量级的web服务器.性能一般 2.IIS 只能在windows平台运行,windows作为服务器在稳定性与其他一些性能上不如类unix操作 ...

  2. Memcached&PHP-Memcache安装配置

    参考文档: memcache官网:https://memcached.org/ 参考:http://www.runoob.com/memcached/memcached-install.html 参考 ...

  3. JS中自定义事件的使用与触发

    1. 事件的创建 JS中,最简单的创建事件方法,是使用Event构造器: var myEvent = new Event('event_name'); 但是为了能够传递数据,就需要使用 CustomE ...

  4. nginx响应client的处理机制

    nginx与apache的不同响应机制——epoll nginx可以处理上百万级别的并发请求就是源至于异步非阻塞的处理机制,异步非阻塞核心即是epoll nginx内部反向代理

  5. PyCharm如何设置源代码字体的大小

    改源代码大小 1.File→Settings→Editor→Colors&Fonts→Font 2.首先得需要Save as一个Scheme,接下来才可以修改字体,名字可以任意取 改运行字体的 ...

  6. 又要开始新的征程了hhh(这次内容比较感兴趣)

    因为做英雄部分,既是我比较感兴趣,又很符合这次c++学习的目的,所以我很开心. 其实从小玩的RPG,即时战略和回合制游戏不算少,对于属性方法其实都算不上陌生.但是还是在网上找了一些学习资源. http ...

  7. 软工实践Beta冲刺答辩

    福大软工 · 第十二次作业 - Beta答辩总结 组长本次博客作业链接 项目宣传视频链接 本组成员 1 . 队长:白晨曦 031602101 2 . 队员:蔡子阳 031602102 3 . 队员:陈 ...

  8. 404 Note Found队——现场编程

    目录 组员职责分工 github 的提交日志截图 程序运行截图 程序运行环境 GUI界面 基础功能实现 运行视频 LCG算法 过滤(降权)算法 算法思路 红黑树 附加功能一 背景 实现 附加功能二(迭 ...

  9. 【IdentityServer4文档】- 使用密码保护 API

    使用密码保护 API OAuth 2.0 协议允许资源拥有者给客户端密码授权:客户端向令牌服务发送用户密码,以获取代表该用户的访问令牌. 该规范建议仅将“资源所有者密码授予”用于“可信”(或旧版)应用 ...

  10. Redis 列表(List)

    Redis列表是简单的字符串列表,按照插入顺序排序.你可以添加一个元素到列表的头部(左边)或者尾部(右边),一个列表最多可以包含2^32-1个元素(4294967295,每个列表超过40亿个元素). ...