malloc超级重要。
void *p1 = malloc(512);
free(p1);
complex* p2 = new complex;//new后面跟的class名称,那么类型就确定了,所以得到的指针也要指向那个类型
delete p2;
void *p3 = ::operator new(512);
::operator delete(p3);//其实就是调用malloc和free
不同编译平台,实现形式略有区别。 borlandc中allocator
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,即自己写的函数,看是否可以释放一些无用内存。
先调用析构函数,然后释放内存。
pc->~Complex();//这里可以直接调用析构函数
operator delete(pc);//void __cdecl operator delete(void *p)_THROW0(){free(p);}
测试:通过指针直接调用构造函数和析构函数(只有vc6让它通过了,正确的不应通过)
Complex* pca = new Complex[3];//唤起三次ctor。无法借由参数给予初值
delete[] pca;//唤起三次dtor。对于整数复数没有指针这一类加不加无所谓
如果不加中括号的话,不知道指针指向的是一个数组,就只会调用一次析构函数。对于复数来讲,它里面没有指针,所以没有内存泄漏,但对于string来说,比如下图右边的例子。
建构由上往下,析构由下往上。
placement new: new(p)A();
placement new 允许我们将对象构建于已经分配好的内存中。因此这一步不分配内存。另外placement new等同于调用构造函数。
因为placement new没有分配内存,因此没有placement delete。
形式:a.new(p)
b.operator new(size,void*);
重载的部分便是管理,比如用一个内存池。
容器不过是将构造函数和析构函数包装在allocate()和deallocate()中了。
operator new 和operator delete一般是静态的,因为一般调用时往往在创建对象过程中,而此时无法通过对象调用,因此必须是static才可以不经过对象就可以调用。
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版本。
核心思想:降低malloc使用次数,因此想先分配一块内存,然后去分小块逐个使用,还想降低cookie的用量。
设计1:为了减少cookie增加了指针。freeStore永远指向未使用的内存的头。
//将一大块分成片片,当做链表串起来,没明白这一步怎么实现的。
for(;p!=&freeStore[screenChunk-1];++p)
p->next=p+1;
union 就是同一个东西用不同名称,从不同角度去表现它。
这一个例子和上述核心是一样的,写法不同,用上了union,借用对象的前4个字节当做指针。嵌入式指针观念。
还的时候并非是用operator delete,而是收回前端。
为了避免每一个类都写一个allocator,我们用一个统一的allocator来包装起来,它里面维护一条链表,但allocator专属于一个类,因此每个类都可以有一个专属链表。有需要内存管理的就去使用它。
大概是个宏定义,typedef去替代一大段代码。
标准库中进一步发展为16根自由链表,并因此不再以application classes内的static呈现,它针对所有类而不是某个类来服务,它是全局的。
new handler的形式和设定方法:
typedef void(*new_handler)(); new_handler set_new_handler(new_handler p) throw();
当operator new没有能力为你分配出你申请的内存时,会抛出一个异常。某些老编译器会返回0。抛出去之前会先调用一个由个人指定的函数。
设计良好的new handler只有两个选择
自己写个函数NoMore()
主函数里调用:set_new_handler(NoMore);
对于operator new和operator delete不能用=default,但是可以delete,不过delete之后那些对应的功能就不可以了。