C++内存管理 侯捷(一)

malloc超级重要。

C++内存管理 侯捷(一)_第1张图片

四个层面的基本用法:

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

complex* p2 = new complex;//new后面跟的class名称,那么类型就确定了,所以得到的指针也要指向那个类型
delete p2;

void *p3 = ::operator new(512);
::operator delete(p3);//其实就是调用malloc和free

C++内存管理 侯捷(一)_第2张图片不同编译平台,实现形式略有区别。                       borlandc中allocator()形成一个对象。有一个缺点是必须记住拿了几个内存,就要还几个。                                                                                         而vc接口比较差,第二个实参没有默认值,而borlandc有默认值,因此没必要写。                                                                                                         gnuc2.9

 基本构件之一——newdelete expression

new expression

new会先分配一块内存,分配好后调用构造函数。

Complex* pc = new Complex(1,2);被编译器转化为:

Complex *pc;
try{
    void* mem = operator new(sizeof(Complex));
    pc = static_cast(mem);//以符合new得到的Complex*
    pc->Complex::Complex(1,2);
//只有编译器才可以像上面那样直接调用ctor
}
catch(std::bad_alloc){
    //若allocation失败就不执行constructor
}

operator new当中有一个while循环,当内存不足时,会进入callnewh中,会调用newhandler,即自己写的函数,看是否可以释放一些无用内存。

delete expression

先调用析构函数,然后释放内存。

pc->~Complex();//这里可以直接调用析构函数

operator delete(pc);//void __cdecl operator delete(void *p)_THROW0(){free(p);}

测试:通过指针直接调用构造函数和析构函数(只有vc6让它通过了,正确的不应通过)

array new/array delete

Complex* pca = new Complex[3];//唤起三次ctor。无法借由参数给予初值

delete[] pca;//唤起三次dtor。对于整数复数没有指针这一类加不加无所谓

如果不加中括号的话,不知道指针指向的是一个数组,就只会调用一次析构函数。对于复数来讲,它里面没有指针,所以没有内存泄漏,但对于string来说,比如下图右边的例子。

建构由上往下,析构由下往上。

placement new:   new(p)A();

C++内存管理 侯捷(一)_第3张图片

 placement new

placement new 允许我们将对象构建于已经分配好的内存中。因此这一步不分配内存。另外placement new等同于调用构造函数。

因为placement new没有分配内存,因此没有placement delete。

形式:a.new(p)

          b.operator new(size,void*);

重载operator new

C++内存管理 侯捷(一)_第4张图片

重载的部分便是管理,比如用一个内存池。

容器不过是将构造函数和析构函数包装在allocate()和deallocate()中了。

operator new 和operator delete一般是静态的,因为一般调用时往往在创建对象过程中,而此时无法通过对象调用,因此必须是static才可以不经过对象就可以调用。

重载placement new

Foo *pf = new(300,'f')Foo;

        我们可以重载class member operator new(),写出多个版本,前提是每个版本都必须有独特的参数列,其中第一个参数必须是size_t,其余参数以new所指定的placement arguments为初值。出现于new(...)小括号内的就是所谓的placement arguments。

        我们也可以重载class member operator delete(),但它们绝不会被delete调用,除非new的ctor抛出异常,否则不会调用重载的版本。

字符串分配的时候会加上一个extra,是因为标准库的字符串有reference counting的功能,所以它后面带了一包东西。这里说的不是字符串会有一个空位给结束标志。所以字符串设计了一个placement new版本。

Per-class allocater

核心思想:降低malloc使用次数,因此想先分配一块内存,然后去分小块逐个使用,还想降低cookie的用量。

设计1:为了减少cookie增加了指针。freeStore永远指向未使用的内存的头。

//将一大块分成片片,当做链表串起来,没明白这一步怎么实现的。
for(;p!=&freeStore[screenChunk-1];++p)
    p->next=p+1;

Per-class allocater2

union 就是同一个东西用不同名称,从不同角度去表现它。

这一个例子和上述核心是一样的,写法不同,用上了union,借用对象的前4个字节当做指针。嵌入式指针观念。

还的时候并非是用operator delete,而是收回前端。

static allocator

为了避免每一个类都写一个allocator,我们用一个统一的allocator来包装起来,它里面维护一条链表,但allocator专属于一个类,因此每个类都可以有一个专属链表。有需要内存管理的就去使用它。

macro for static allocator

大概是个宏定义,typedef去替代一大段代码。

标准库中进一步发展为16根自由链表,并因此不再以application classes内的static呈现,它针对所有类而不是某个类来服务,它是全局的。

new handler

new handler的形式和设定方法:

typedef void(*new_handler)();
new_handler set_new_handler(new_handler p) throw();

 

当operator new没有能力为你分配出你申请的内存时,会抛出一个异常。某些老编译器会返回0。抛出去之前会先调用一个由个人指定的函数。

设计良好的new handler只有两个选择

  • 让更多的memory可用
  • 调用abort()或exit()

自己写个函数NoMore()

主函数里调用:set_new_handler(NoMore);

对于operator new和operator delete不能用=default,但是可以delete,不过delete之后那些对应的功能就不可以了。

 

你可能感兴趣的:(C++内存管理 侯捷(一))