C++中的内存分配
来源:互联网 发布:淘宝上有纯粮食酒吗 编辑:程序博客网 时间:2024/06/05 11:51
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(0),first_free(0),end(0){} 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 = 2* max(size,1);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 expression2 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();//placement4 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(2);// allocate space to hold 2 strings3 new(sp) string(b,e);4 alloc.construct(sp +1, 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 =0; 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 != 0)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,它的内存布局类似这样:
0 0
- c中的内存分配
- C语言中的内存分配
- C中的动态内存分配
- C语言中的内存分配
- C语言中的内存分配
- C语言中的内存分配
- C语言中的内存分配
- C语言中的内存分配深入(一)
- C语言中的内存分配深入(二)
- C语言中的内存地址分配模型
- C语言中的内存分配深入
- C语言中的初始化及内存分配
- c语言中的动态内存分配~
- C语言中的动态内存分配函数
- c语言中的动态内存分配
- C 语言中的内存分配介绍
- 一个c/c++程序中的内存分配
- C/C++中的内存地址分配问题
- 关于UIWindow的一点儿思考
- nginx +phpfastcgi 环境下 导出excel文件,超时,数据被截断问题,解决
- 用哈弗曼编码实现文件压缩和解压
- vim配置
- SIMD并行
- C++中的内存分配
- iOS截屏“闪现效果实现”
- 算法导论-最短路径-Dijkstra算法+Bellman-Ford 算法
- BezierDemo开源项目的学习
- ios学习day—1
- keepalived的双主搭建
- 《leetCode》:Symmetric Tree
- Tomcat与Eclipse集成
- PHP面试题