Cpp内存管理

从基本的Cpp内存管理构件到高级内存管理器

Doug Lea自1986年起就潜心研究malloc算法

零 overview 内存分配的每一个层面

在这里插入图片描述

一 从C++ primitives介绍起

在这里插入图片描述

1 这几种基础原件的使用

// malloc and free
void *p1 = malloc(512);
free(p1);

// new and delete
complex<int>* p2 = new complex<int>;
delete p2;

//::operator new and :: operator delete
void *p3 = :: operator new(512);      //其实内层还是malloc and free
::operator delete(p3);

//allocator from cpp standrad lib
#ifdef __GNUC__
void* p4 = alloc::allocate(512);
alloc::dellocate(p4,512);
#endif __GNUC__

2 new / operator new / malloc 三者的关系

在上一节中已经说过,new expression的详细过程

new 操作符的执行过程:

  1. 调用operator new分配内存 ;
  2. 调用构造函数在operator new返回的内存地址处生成类对象;(用户是不能做这个动作的 vc6可以gcc不可以(vc6在这方面还是不严谨) )

operator new 中使用了malloc来分配内存,如果内存不够分配了malloc即将失败,会调用__callnewh来释放一部分内存,这个机制是自己可以设置的.
如果你真的想直接调用ctor,换个思路,可以运用placement new:new( p ) Complex(1,2);

其兄弟delete expression:

  1. 先调用析构函数:pc->~Complex();
  2. 调用operator delete(pc); 释放内存 //析构函数可以被直接调用

operator delete 中使用了free来释放内存

3 array new/delete expression

Complex* pca = new Complex[3];   //唤起3次ctor
delete[] pac;                    //唤起3次dtor 如果不带[] 就会引起内存泄露

对于这类一次性new/delete多个对象的,其使用一次new/delete,底层都会唤起多次ctor/dtor,同时this会指向不同的object. 至于如何实现这个机制,那就要谈谈cookie的使用,你可以把cookie看做malloc和free的约定,在malloc分配内存时会在那一块内存边上附带一个cookie,其标记着something about bookkeeping,保存着其中分配了多少个对象.dtor被唤起时,free会获取到这个数值,从而dtor会被唤醒这么多次数,同时this指针也会跟着变动,使得每次都能都指向不同的object
在这里插入图片描述
这种使用 new object[n] 同时实例化多个对象的方法有个缺点,那就是无法使用参数设初值,可以用循环搭配placement new来初始化

4 placement new

placement new 允许我们将object建构于allocated memory中
没有所谓的placement delete ,因为placement new 根本没有分配memory
更倾向与把placement理解为一种定点的构造函数,在分配好的内存上调用构造函数

placement new只是operator new重载的一个版本。它并不分配内存,只是返回指向已经分配好的某段内存的一个指针。因此不能删除它,但需要调用对象的析构函数。
如果你想在已经分配的内存中创建一个对象,使用new时行不通的。也就是说placement new允许你在一个已经分配好的内存中(栈或者堆中)构造一个新的对象。原型中void* p实际上就是指向一个已经分配好的内存缓冲区的的首地址。

#include <new>

char* buf = new char[sizeof(Complex) *3];
Complex* pc = new(buf) Complex(1,2);
delete [] buf;

/*new(buf)那一行 被编译器转化为:*/

Complex *pc;
try{
 void* mem = operator new(sizeof(Complex),buf);  //void* operator new(size_t size,void* loc ) {return loc;}
 pc = static_cast<Complex*> (mem);
 pc->Complex::Complex(1,2);
 }
catch(std::bad_alloc)
{
	//若allocation失败就不执行constructor
}

1.用placement new 解决buffer的问题

问题描述:用new分配的数组缓冲时,由于调用了默认构造函数,因此执行效率上不佳。若没有默认构造函数则会发生编译时错误。如果你想在预分配的内存上创建对象,用缺省的new操作符是行不通的。要解决这个问题,你可以用placement new构造。它允许你构造一个新对象到预分配的内存上。

2.增大时空效率的问题

使用new操作符分配内存需要在堆中查找足够大的剩余空间,显然这个操作速度是很慢的,而且有可能出现无法分配内存的异常(空间不够)。placement new就可以解决这个问题。我们构造对象都是在一个预先准备好了的内存缓冲区中进行,不需要查找内存,内存分配的时间是常数;而且不会出现在程序运行中途出现内存不足的异常。所以,placement new非常适合那些对时间要求比较高,长时间运行不希望被打断的应用程序。

彻底了解手上的三把刀子之后

C++应用程序,分配内存的途径:

//应用程序

//使用表达式 (new/delete expression) 不可改变 不可重载
Foo* p = new Foo(x);
---
delete p;

//将上面的new expression详细解释起来就是
Foo* p = (Foo*) operator new(sizeof(Foo)); //------->没有重载时调用全局的 ::operator new(size_t)  里面就是 CRT FUNC:malloc() (delete亦然,可以重载但是很少见)
new(p) Foo(x);
---
p->~Foo();
operator delete(p):

//所谓内存管理的一部分事情就是在重载这个operator new/delete 接管这个动作 
//在类中重载 优先级更高
//可以考虑做一个内存池 
//思考cookie的耗用情况
Foo::operator new(size_t);

在这里插入图片描述

让我们试图简单重载试试:

这里我们在类X中重载了operator new,这是最常见的做法。
在这里插入图片描述
运行结果为:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值