C++提供下面两种方法分配和释放未构造的原始内存
(1)allocator 类,它提供可感知类型的内存分配
(2)标准库中的 operator new 和 operator delete,它们分配和释放需要大小的原始未类型化的内存
 
C++ 还提供不同的方法在原始内存中构造和撤销对象
(1)std::allocator<T>::construct 在未构造内存中初始化对象,std::allocator<T>::destroy 在对象上运行适当的析构函数。
(2)定位 new 表达式( placement new expression),接受指向未构造内存的指针,并在该空间中初始化一个对象或一个数组。
(3)直接调用析构函数来撤销对象
(4)算法 uninitialized_fill 和 uninitialized_copy 像 fill 算法和 copy 算法一样执行,除了它们在目的地构造对象而不给对象赋值之外。

std::allocator<T>

allocator类将内存的分配及对象构造分开。分配及释放分别是 allocate 和 deallocate 。构造和析构分别是 construct 和 destroy。

std::vector<T>

标准库中的 vector 是一个动态数组。在使用时,vector 预先分配一块内存,当数据增长到预先分配的内存不够使用时,vector 采取的策略是重新分配 2 倍于当前 vector 容量大小的内存,并把所有数据复制到新地址处,旧地址所有数据进行析构,最后将这块旧的内存释放。
    1. template<class T>
    2. class vector
    3. {
    4. public:
    5. vector():elements(),first_free(),end(){}
    6. void push_back(const T&);
    7. //…
    8. private:
    9. static std::allocator<T> alloc;
    10. void reallocate();
    11. T* elements;
    12. T* first_free;
    13. T* end;
    14. //…
    15. };

elements 指向数组的第一个元素,first_free 指向第一个空闲的位置,end 指向数组最一个元素的下一个元素。

std::vector<T>::push_back(const T& t)

    1. template <class T>
    2. void vector<T>::push_back(const T& t)
    3. {
    4. //are we out of space
    5. if(first_free == end)
    6. reallocate();// gets more space and copies existing elements to it
    7. alloc.construct(first_free,t);
    8. ++first_free;
    9. }
    10. template <class T>
    11. void vector<T>::reallocate()
    12. {
    13. std::ptrdiff_t size = first_free elements;
    14. std::ptrdiff_t newcapacity = * max(size,);
    15.  
    16. T* newelements = alloc.allocate(newcapacity);
    17.  
    18. uninitialized_copy(elements,first_free,newelements);
    19.  
    20. for(T *p = first_free; p != elements;}
    21. alloc.destroy(--p);
    22.  
    23. if(elements)
    24. alloc.deallocate(elements,end-elements);
    25. elements = newelements;
    26. first_free = elements + size;
    27. end = elements + newcapacity;
    28. }
 

operator new 函数和 operator delete 函数

 
理解 new 表达式
    1. //new expression
    2. string *sp =new string(“initialized”);

    首先,该表达式调用名为 operator new 的标准库函数,分配足够大的原始的未类型化的内存;

接下来,运行该类型的一个构造函数,用指定初始化式构造对象;
最后,返回指向新分配并构造的对象的指针
 
    1. delete sp;

    首先,对 sp 指向的对象运行适当的析构函数

然后,通过调用名为 operator delete 的标准库函数释放该对象所用内存
 

operator new 和 operator delete 接口

    1. void*operator new(size_t);
    2. void*operator new[](size_t);
    3. void*operator new(std::size_t size,void* ptr)throw();//placement
    4.  
    5. void*operator delete(void*);
    6. void*operator delete[](void*);
与 allocator 类的 allocate 和 deallocate 成员类似。
 
但是,operator new 和 operator delete 在 void* 指针而不是类型化的指针上进行操作。这就意味着,allocate 成员分配类型化的内存,不必计算以字节为单位的所需内存量,它们也可以避免对 operator new 的返回值进行强制类型转换
 

placement new 定位 new 表达式

 
placement new 在已分配的原始内存中初始化一个对象。它与 new 的其它版本的不同之处在于,它不分配内存。
 
定位 new 表达式的形式是:
new (place_address) type
new (place_address) type (initializer-list)
 
例:
    1. std::allocator<std::string> alloc;
    2. string *sp = alloc.allocate();// allocate space to hold 2 strings
    3. new(sp) string(b,e);
    4. alloc.construct(sp +, string(b,e));
总结
 

类特定的 new 和 delete

默认情况下, new 表达式通过调用由标准库定义的 operator new 版本分配内存。通过定义自己的名为 operator new 和 operator delete 的成员,类可以管理用于自身类型的内存。
 
编译器看到类类型的 new 或 delete 表达式的时候,它查看该类是否有 operator new 或  operator delete 成员,如果类定义(或者继承)了自己的成员 new 和 delete 函数,则使用那些函数为对象分配和释放内存;否则,调用这些函数的标准库版本。
 

一个内存分配器基类

通用策略:
预先分配一块原始内存来保存未构造的对象,创建新元素时,可以在一个预先分配的对象中构造;释放元素的时候,将它们放回预先分配对象的块中,而不是将内存实际返回给操作系统。
 
这种策略常被称为维持一个自由列表(freelist)。可以将自由列表实现为已分配但未构造的对象的链表。
 
    1. template<class T>
    2. class CachedObj
    3. {
    4. public:
    5. void* operator new(std::size_t);
    6. void* operator delete(void*, std::size_t);
    7. virtual ~CachedObj(){}
    8. protected:
    9. T* next;
    10. private:
    11. static void add_to_freelist(T*);
    12. static std::allocator<T> alloc_mem;
    13. static T* freeStore;
    14. static const std::size_t chunk;
    15. }
CacheObj 只是分配和管理已分配但未构造对象的自由列表。
 
定义 operator new,返回自由列表的下一个元素,并将该元素从自由列表中删除。当自由列表为空的时候,operator new 将分配新的原始内存。
定义 operator delete,在撤销对象时将元素放回自由列表。
 
实现:
    1. template<class T>
    2. void*CachedObj<T>::operator new(size_t sz)
    3. {
    4. if(sz != sizeof(T)) return nullptr;
    5. if(!freeStore)
    6. {
    7. T* array = alloc_mem.allocate(chunk);
    8. for(size_t i =; i != chunk;++i)
    9. add_to_freelist(&array[i]);
    10. }
    11. T*p = freeStore;
    12. freeStore = freeSore->CachedObj<T>::next;
    13. return p;
    14. }
    15. template<class T>
    16. void CachedObj<T>::add_to_freelist(T*p)
    17. {
    18. p->CachedObj<T>::next = freeStore;
    19. freeStore = p;
    20. }
    21. template<class T>
    22. void CachedObj<T>::operator delete(void*p)
    23. {
    24. if(p != )
    25. add_to_freelist(static_cast<T*>(p));
    26. }
 
CachedObj 其实很像 linux 的链表。除掉 private 的 static 变量和一个虚表的指针,CachedObj 其实只维护一个 T* next。它的内存布局像这样:
当继承 CachedObj 类的时候,用来实例化 CachedObj 类的模板类型将是派生类型本身。
 
其实就是利用模板很巧妙地把 next 指针的类型设置成子类的类型。实例化出来的类仿佛本就是一个类,没有两个类拼凑的感觉,天衣无缝。 
 
例如:
    1. template<classType>
    2. class QueueItem:
    3. publicCachedObj<QueueItem<Type>>{};
QueueItem 继承自 CachedObj,它的内存布局类似这样:

C++中的内存分配的更多相关文章

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

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

  2. rt-thread中动态内存分配之小内存管理模块方法的一点理解

    @2019-01-18 [小记] rt-thread中动态内存分配之小内存管理模块方法的一点理解 > 内存初始化后的布局示意 lfree指向内存空闲区首地址 /** * @ingroup Sys ...

  3. Java基础-Java中的内存分配与回收机制

    Java基础-Java中的内存分配与回收机制 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一. 二.

  4. java中子类实例化过程中的内存分配

    知识点: 子类继承父类之后,实例化子类时,内存中子类是如何分配内存的呢? 下面,自己会结合一个例子,解释一下,一个子类实例化过程中,内存是如何分配的 参考博客:http://www.cnblogs.c ...

  5. Java实例化对象过程中的内存分配

    Java实例化对象过程中的内存分配: https://blog.csdn.net/qq_36934826/article/details/82685791 问题引入这里先定义一个很不标准的“书”类,这 ...

  6. Netty 中的内存分配浅析

    Netty 出发点作为一款高性能的 RPC 框架必然涉及到频繁的内存分配销毁操作,如果是在堆上分配内存空间将会触发频繁的GC,JDK 在1.4之后提供的 NIO 也已经提供了直接直接分配堆外内存空间的 ...

  7. Netty 中的内存分配浅析-数据容器

    本篇接续前一篇继续讲 Netty 中的内存分配.上一篇 先简单做一下回顾: Netty 为了更高效的管理内存,自己实现了一套内存管理的逻辑,借鉴 jemalloc 的思想实现了一套池化内存管理的思路: ...

  8. C语言中动态内存分配的本质是什么?

    摘要:C语言中比较重要的就是指针,它可以用来链表操作,谈到链表,很多时候为此分配内存采用动态分配而不是静态分配. 本文分享自华为云社区<[云驻共创]C语言中动态内存分配的本质>,作者: G ...

  9. iOS程序中的内存分配 栈区堆区全局区

    在计算机系统中,运行的应用程序的数据都是保存在内存中的,不同类型的数据,保存的内存区域不同.一.内存分区 栈区(stack) 由编译器自动分配并释放,存放函数的参数值,局部变量等.栈是系统数据结构,对 ...

  10. c++中函数中变量内存分配以及返回指针、引用类型的思考

    众所周知,我们在编程的时候经常会在函数中声明局部变量(包括普通类型的变量.指针.引用等等). 同时,为了满足程序功能的需要,函数的返回值也经常是指针类型或是引用类型,而这返回的指针或是引用也经常指向函 ...

随机推荐

  1. [LC] 318. Maximum Product of Word Lengths

    Given a string array words, find the maximum value of length(word[i]) * length(word[j]) where the tw ...

  2. C++对数组进行复制

    C++ 风格的复制操作 使用STL中的copy算法 int a[] = {1,2,3,4,5}; int b[5]; std::copy(std::begin(a),std::end(a),std:: ...

  3. 如何创建Hexo站点的Tags和Categories默认页面

    安装Hexo的categories生成插件 1 $ npm install hexo-generator-category --save 安装Hexo的Tags生成插件 1 $ npm install ...

  4. 吴裕雄--天生自然python学习笔记:Python3 标准库概览

    操作系统接口 os模块提供了不少与操作系统相关联的函数. >>> import os >>> os.getcwd() # 返回当前的工作目录 'C:\\Python ...

  5. spring配置ConcurrentMap实现缓存

    spring本身内置了对Cache的支持,本次记录的是基于Java API的ConcurrentMap的CacheManager配置. 1.xml文件中增加命名空间 <beans xmlns=& ...

  6. Nginx笔记总结三:内核参数优化

    net.ipv4.netfilter.ip_conntrack_tcp_timeout_established = 1800 net.ipv4.ip_conntrack_max = 16777216 ...

  7. Java 内部类(成员内部类、局部内部类、静态内部类,匿名内部类)

    一.什么是内部类? 内部类是指在一个外部类的内部再定义一个类.内部类作为外部类的一个成员,并且依附于外部类而存在的.内部类可为静态,可用protected和private修饰(而外部类只能使用publ ...

  8. 默认的Settings.xml文件(无修改过)-Maven

    Tip: 当什么都不作修改时,默认是从Maven中央仓库进行下载,https://repo.maven.apache.org/maven2. 打开maven源码下的lib文件夹,找到maven-mod ...

  9. python 常用模块介绍

    1.定义 模块:用来从逻辑上组织python代码(变量.函数.类,逻辑),本质就是.py结尾的python文件(文件名:test.py,对应的模块名:test). 包:用来从逻辑上组织模块的,本质就是 ...

  10. js中判断为false的情况

     document.write((new Boolean())+"<br />");        document.write((new Boolean(" ...