Complex* pc = new Complex(1, 2)可以被编译器看成什么呢?看成如下一系列的操作(这里有分配内存,所以必须关心分配内存失败了该怎么办,所以有try-catch):
Complex *pc;try{void* mem =operatornew(sizeof(Complex));//allocate
pc =static_cast<Complex*>(mem);//cast
pc->Complex::Complex(1,2);//construct}catch(std::bad_alloc){//若allocation失败就不执行constructor}
Foo* p =newFoo(x);//被转化为下面的两个动作
Foo* p =(Foo*)operatornew(sizeof(Foo));new(p)Foo(x);//既然底层是这两个动作,那么直接写是否可以?可以的,但是没必要//那么operator new()底层是什么呢?::operatornew(size_t);//这就是operator new()的底层//那么::operator new()底层是什么呢?malloc(size_t);//底层是malloc
那么像 Foo* pf = new(300, 'c')Foo; 算不算是
p
l
a
c
e
m
e
n
t
placement
placement
n
e
w
new
new 呢?现在统一术语,带着小括号的都是
p
l
a
c
e
m
e
n
t
placement
placement
n
e
w
new
new
但是每一个这样的版本,都需要有独特的参数列
其中的第一参数是 size_t
其余的参数以
p
l
a
c
e
m
e
n
t
placement
placement
a
r
g
u
m
e
n
t
argument
argument 为设计,例如 (300, 'c') 就是对应的
p
l
a
c
e
m
e
n
t
placement
placement
a
r
g
u
m
e
n
t
argument
argument
先看
d
a
t
a
data
data 版本,一个
u
n
s
i
g
n
e
d
unsigned
unsigned
l
o
n
g
long
long 还有一个
c
h
a
r
char
char,加起来是
5
5
5 个字节,但是编译器一般都会设置对齐,齐位,所以这
5
5
5 个字节会被调整成
8
8
8 个字节,这
5
5
5 个字节包装成一个
s
t
r
u
c
t
struct
struct
还有一个
u
n
i
o
n
union
union,
u
n
i
o
n
union
union 就是同一个东西用不同的名称去表现他,
u
n
i
o
n
union
union 这里有两个
m
e
m
b
e
r
member
member,但是这两个
m
e
m
b
e
r
member
member 其实是同一块东西,一个
m
e
m
b
e
r
member
member 就是刚刚的那
5
5
5 个字节(齐位成了
8
8
8 个字节),另外一个
m
e
m
b
e
r
member
member 就是把他看成一个指针,也就是说本来
8
8
8 个字节,现在看成指针(
4
4
4 个指针),也就是只看前
4
4
4 个字节
作用其实是与第一个版本差不多的,但是,好就好在,这里使用了
u
n
i
o
n
union
union,
u
n
i
o
n
union
union 当中的指针,是借用
s
t
r
u
c
t
struct
struct
A
i
r
p
l
a
n
e
R
e
p
AirplaneRep
AirplaneRep 当中的前
4
4
4 个字节来当指针用,这种做法太妙了,减少了空间的浪费
如果有很多个
c
l
a
s
s
class
class,那么就需要写很多套上面示例
2
2
2 的
a
l
l
o
c
a
t
o
r
allocator
allocator,这里重复性动作太多,从内存的角度来看并不合适,同样的内容,不应该集中到一个地方去吗?
c
l
a
s
s
A
class A
classA 写一套,
B
C
D
E
BCDE
BCDE 又写一套,重复内容太多了
这次改进,就把重复的内容抽取出来,放到一个
c
l
a
s
s
class
class 里面,这个
c
l
a
s
s
class
class 叫做
a
l
l
o
c
a
t
o
r
allocator
allocator
下方,
a
l
l
o
c
a
t
o
r
allocator
allocator 就想象成一个指针,指向一条链表,专门为
c
l
a
s
s
class
class 服务,下方的两个
m
e
m
b
e
r
member
member
f
u
n
c
t
i
o
n
function
function 的
n
e
w
、
d
e
l
e
t
e
new、delete
new、delete 就不做了,交给
a
l
l
o
c
a
t
o
r
allocator
allocator 的实例化去做
在下方,
a
l
l
o
c
a
t
o
r
allocator
allocator 为什么要设置成
s
t
a
t
i
c
static
static?因为这个
a
l
l
o
c
a
t
o
r
allocator
allocator 是专门为这个
c
l
a
s
s
class
class 服务的,所以设置成
s
t
a
t
i
c
static
static
意思就是每一个
c
l
a
s
s
class
class 内部都有一个专门为其服务的
a
l
l
o
c
a
t
o
r
allocator
allocator,这个
a
l
l
o
c
a
t
o
r
allocator
allocator 里面就有一个单向链表,链表每个区块就是
c
l
a
s
s
class
class 的大小
2.4 版本4:macro static allocator
就是将左手边黄色部分的代码变成右手边蓝色部分的代码
就是规范一下制式,这就是
m
a
c
r
o
macro
macro
s
t
a
t
i
c
static
static
a
l
l
o
c
a
t
o
r
allocator
allocator
设计好
m
a
c
r
o
macro
macro
s
t
a
t
i
c
static
static
a
l
l
o
c
a
t
o
r
allocator
allocator,就是 #define DECLARE_POOL_ALLOC()
具体如下,放到 define 里面去,为了偷懒doge
// in class definition fileclassFoo{DECLARE_POOL_ALLOC()public:long L;
string str;public:Foo(long l):L(l){}};//in class implementation fileIMPLEMENT_POOL_ALLOC(Foo)// in class definition fileclassGoo{DECLARE_POOL_ALLOC()public:
complex<double> c;
string str;public:Goo(const complex<double>& x):c(x){}};//in class implementation fileIMPLEMENT_POOL_ALLOC(Goo)
重要的是,
C
+
+
C++
C++ 在抛出异常之前,会先调用一个自我设定的函数(不止一次的调用),比如说:typedef void(*new_handler)(); new_handler set_new_handler(new_handler p) throw();
这种形式:返回值是
v
o
i
d
void
void,没有参数的函数,只要抛出异常之前,都会调用你这个函数,所以
C
+
+
C++
C++ 通过调用你的函数,来通知你,由你来决定怎么办
在
V
C
VC
VC 底下,operator new 的源代码,这里面就有 while loop 不断调用 malloc
所以,不管什么形式,用 new 也好,operator new 也好,最终都是跑到 malloc,一旦失败,按上面的情况就是抛出异常咯?但是会先调用 typedef void(*new_handler)(); new_handler set_new_handler(new_handler p) throw();,调用完了之后,
V
C
VC
VC 底下叫 _callnewh ,顾名思义就是调用 new handler,调用完了回来再分配一次,就是看看你有没有什么补救措施,既然调入了 new handler,就代表没有内存可用了,释放完了之后,再调用new handler,看看有没有内存可用
所以说,设计良好的 new handler 只有两个选择:
让更多 memory 可用
调用 abort() 或 exit()
2.6.1 举例
#include<new>#include<iostream>#include<cassert>namespace jj13
{voidnoMoreMemory(){
cerr <<"out of memory";abort();}voidtest_set_new_handler(){
cout <<"\n\n\ntest_set_new_handler().......... \n";set_new_handler(noMoreMemory);/*
int* p = new int[100000000000000]; //well, so BIG!
assert(p);
p = new int[100000000000000000000]; //[Warning] integer constant is too large for its type
assert(p);
*/}}//namespace
看,这里使用了 abort(),符合上述两个条件之一
2.7 = default, = delete
default 就是默认
这两个不只适用于构造,析构,等,还适合 operator new/delete, []
operator new/delete 怎么有
d
e
f
a
u
l
t
default
default 版本?
3 std::allocator 标准库的 allocator
3.1
V
C
6
VC6
VC6malloc()
malloc() 所得到的区块,是带着 cookies 的,但是一次又一次的 malloc,就会一直产生不必要的 cookies
那么如何去除 cookies ?
3.2
V
C
6
VC6
VC6allocator 实现
classallocator{private:structobj{structobj* next;//embedded pointer};public:void*allocate(size_t);voiddeallocate(void*, size_t);voidcheck();private:
obj* freeStore =nullptr;constint CHUNK =5;//小一點方便觀察 };void* allocator::allocate(size_t size){
obj* p;if(!freeStore){//linked list 是空的,所以攫取一大塊 memory
size_t chunk = CHUNK * size;
freeStore = p =(obj*)malloc(chunk);//cout << "empty. malloc: " << chunk << " " << p << endl;//將分配得來的一大塊當做 linked list 般小塊小塊串接起來for(int i=0; i <(CHUNK-1);++i){//沒寫很漂亮, 不是重點無所謂.
p->next =(obj*)((char*)p + size);
p = p->next;}
p->next =nullptr;//last }
p = freeStore;
freeStore = freeStore->next;//cout << "p= " << p << " freeStore= " << freeStore << endl;return p;}void allocator::deallocate(void* p, size_t){//將 deleted object 收回插入 free list 前端((obj*)p)->next = freeStore;
freeStore =(obj*)p;}void allocator::check(){
obj* p = freeStore;int count =0;while(p){
cout << p << endl;
p = p->next;
count++;}
cout << count << endl;}//--------------
最主要的就是
a
l
l
o
c
a
t
o
r
allocator
allocator 与
d
e
a
l
l
o
c
a
t
o
r
deallocator
deallocator 那两个成员函数
V
C
VC
VC 的分配器当中是以
i
n
t
int
int 为分配单位的,这与之后的
G
N
U
C
GNUC
GNUC 不太一样
G
N
U
C
GNUC
GNUC 是以
B
y
t
e
s
Bytes
Bytes 为单位的
3.3
B
O
L
A
N
C
BOLANC
BOLANCallocator 实现
在
B
O
L
A
N
C
BOLANC
BOLANC 里面,使用的分配器也是什么都没有做,还是
m
a
l
l
o
c
malloc
malloc 和
f
r
e
e
free
free
而且现在分配内存都是使用容器,发现容器分配出来的空间,每一块都带有
c
o
o
k
i
e
s
cookies
cookies ,而我们现在就是要去除这些
c
o
o
k
i
e
s
cookies
cookies
去除
c
o
o
k
i
e
s
cookies
cookies 有一个先决条件,就是区块要有一样的大小,如果区块有大有小,你怎么去除
c
o
o
k
i
e
s
cookies
cookies?因为
c
o
o
k
i
e
s
cookies
cookies 就是在记录区块的大小,你怎么去除他?所以为了去除
c
o
o
k
i
e
s
cookies
cookies,就必须把区块设置成一样大小,这样
c
o
o
k
i
e
s
cookies
cookies 才能被去除
3.4
G
N
U
C
GNUC
GNUCallocator 实现
2.9 版本的
G
N
U
C
GNUC
GNUC 的分配器实现,也不过就是这样而已
不要使用<defalloc.h>这个头文件
又说
G
N
U
C
GNUC
GNUC 使用的是不同的
a
l
l
o
c
a
t
o
r
allocator
allocator
现在的这个头文件,并没有被 include 到任何容器里
分配器用的都是
a
l
l
o
c
alloc
alloc
a
l
l
o
c
alloc
alloc 是一个类
alloc::allocate 是 alloc 的一个静态函数
alloc::allocate(512) 这里指的是
512
512
512 字节
在2.9版本的
a
l
l
o
c
alloc
alloc 到了 4.9 版本就换成了
p
o
o
l
a
l
l
o
c
_pool_alloc
poolalloc
并且变成了一个编制外(编制内的就是标准的
a
l
l
o
c
a
t
o
r
allocator
allocator)的东西,把好东西(
a
l
l
o
c
alloc
alloc)换掉了
3.5
G
N
U
C
GNUC
GNUC 标准 allocator 实现
标准分配器就是 std::allocator
在 4.9 版本当中,
a
l
l
o
c
a
t
o
r
allocator
allocator 继承了
a
l
l
o
c
a
t
o
r
b
a
s
e
_allocator_base
allocatorbase,然后
a
l
l
o
c
a
t
o
r
b
a
s
e
_allocator_base
allocatorbase 有一个 define
#define _allocator_base __gnu_cxx::new_allocator
而这个new_allocator 就是上面的 new_allocator 这个类
所以说,相当于 std::allocator 继承了 new_allocator
4.9版的标准
a
l
l
o
c
a
t
o
r
allocator
allocator 也没有很大的改动,还是 ::operator new 以及 operator::delete