c++ 内存管理 1

  • new/[], delete/[] 解析,placement new定点分配
  • new handler
  • per-class allocator
  • static allocator
  • std::alloc
  • std::alloc_pool

new/[], delete/[] 解析

Complex* c = new Complex(1,2);
1. 分配内存
2. 指针转型
3. 调用对象构造函数

try{
        void* mem = operator new(sizeof(Complex));
        pc = static_cast<Complex*>(mem);
        pc->Complex::Complex(1,2);//不可以直接调用想要直接调用构造函数可以使用placement new(new(p)Complex(1,2))
}catch(std::bad_alloc){

}
void*operator new(size_t size,const std::nothrow _t&)_THROW()
{
    void *p;
    while((p==malloc(size)) ==0){
        _TRY_BEGIN
            if(_callnewh(size) ==0) break;//分配失败调用new handler 可以自定义
        _CATCH(std::bad_alloc) return 0;
        _CATCH_END
    }
    return p;
}

delete 分两个步骤
delete pc
1. 调用析构函数
2. 释放空间

pc->~Compelx();
operator delete(pc);
void __cdecl operator delete(void* p){
    free(p);
}

补充:__cdecl __stdcall区别,一个是调用这清理栈,一个是自身清理
http://blog.csdn.net/j_jeff/article/details/41693737
array new

complex* pca = new Complex[3];
delete[] pca;//如果只调用delete pca,则不会调用析构函数有可能会造成内存泄露(对象管理的那部分内存 即指针指向的)

new /delete调用路径

//1
Foo* p = newFoo(x);
delete p;
//2
Foo* p = (Foo*)operator new(sizeof(Foo));
new(p)Foo(x);

p->~Foo();
operator delete(p);
//可以调用member new/delete
Foo::operator new(size_t);
Foo::operator delete(void*);
//全局new/delete
::operator new(size_t);
::operator delete(void*);
//3
malloc(size_t);
free(void*)

例子重载全局operator new/delete

//注意不可以方在namespace中
void* myAlloc(size_t size){return malloc(size);}
void myFree(void* ptr){ free(ptr);}
inline void* operator new(size_t size){
    return myAlloc(size);
} 
inline void* operator[](size_t size){return myAlloc(size);}
inline void operator delete(void* ptr){myFree(ptr);}
inline void operator delete[](void* ptr){myFree(ptr);}

重载class member operator new() 可以重载多个,但是每一个版本的声明第一个参数必须是size_t,其余的采纳湖一new所指定的placement arguments 为初始值。出现于new(args) args即所谓的placement arguments.
Foo* pf = new(330,’c’)Foo;
也可以重载per-class operator delete 但是只有在GNU2.9之前当ctor出现异常时才会调用,4.9之后即便ctor出现异常也不会调用了。

placement new定点分配

//示例
class Foo{
public:
    Foo(){cout << "Foo::Foo" <<endl;}
    Foo(int){cout << "Foo::Foo(int)" << endl;}
    void* operator new(size_t size){
        return malloc(size);
    }
    //标准库提供的placement new 形式
    void*operator new(size_t size,void* start){
        return start;
    }
    void* operator new(size_t size, long extra){
        return malloc(size + extra);
    }
    void* opeartor new(size_t size,long extra,char init){
        return malloc(size + extra);//size_t 必须在最前面,long不可以
    }

};

注意所有 class中的new,delete默认都是static的要在构造之前就调用

new handler

当opeartor new 分配内存失败时会抛出一个bad_alloc exception ,抛出之前可以指定一个handler来处理:

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

//可以通过调用set_new_handler 来指定处理函数
//一般只有两种处理方式1. 让更多的memory可用,2.调用abort()或者exit()

参考:
http://www.cnblogs.com/jerry19880126/p/3722531.html

per-class allocator

class Screen{
public:
    Screen(int x):_i(x){};
    int get(){return _i;}
    void*operator new(size_t);
    void operator delete(void*, size_t);
private:
    Screen* next;
    static Screen* freeStore;
    static const int screenChunk;
private:
    int _i;
};
Screen* Screen::freeStore = 0;
const int Screen::screnChunk = 24;

void* Screen::operator new(size_t size)
{
    Screen *p;
    if(!freeStore){
    //linked list 是空的所以申请一大块
    size_t chunk = screenChunk * size;
    freeStore = p =
        reinterpret_cast<Screen*>(new char[chunk]);
        //将大块内存分割成片,当作linked list 串起来备用。
        for(;p!=freeStore[screenChunk -1];++p)
            p->next = p+1;
        p->next = nullptr;
    }
    p = freeStore;
    freeStore = freeStore->next;
    return p;
}

void Screen::operator delete(void* p;size_t size)
{
    //将deleted object 插回freelist
    (static_cast<Screen*>(p)) ->next = freeStore;
    freeStore = static_cast<Screen*>(p); 
}

例2

Class Airplane{
private:
    struct AirPlaneRep{
        unsigned long miles;
        char type;
    };
private:
    union{
        AireplaneRep rep;//针对使用中的object
        AirePlane* next;//针对freelist上的object
    };
public:
    unsigned long getMiles(){return rep.miles;}
    char getType(){return rep.types;}
    void set(unsigned long m, char t)
    {
        rep.miles=m; rep.type=t;
    }
public:
    static void* operator new(size_t size);
    static void operator delete(void* deadObject,size_t size);
private:
    static const int BLOCK_SIZE;
    static Airplane* headOfFreeList;
};
Aireplane* Airplane::headOfFreeList = nullptr;
const int Airplane::BLOCK_SIZE = 512;
void* Airplane::operator new(size_t size)
{
    //如果大小有误转交给::operator new;(当继承发生时)
    if(size !=sizeof(Airplane))
        return ::operator new(size);
    Airplane* p = headOfFreeList;
    if(p)//如果p有效就把头部下移一位传回去
        headOfFreeList = p->next;
    else{
        //freelist 已空,就申请一块大内存
        Airplane* newBlock = static_cast<Airplane*>
        (::operator new(BLOCK_SIZE*sizeof(Airplane)));
        //将小块串起来避开第一个位置(已经使用需要回传)
        for(int i = 1; i < BLOCK_SIZE -1; ++i)
            newBlock[i].next = &newBlock[i+1];
        newBlock[BLOCK_SIZE-1] = nullptr;//list 结束
        p=newBlock;
        headOfFreeList = &newBlock[1];
    }
    return p;
}
void Airplane::operator delete(void* deadObject,size_t size){
    if(deadOnject == nullptr)return;
    if(size != sizeof(Airplane))
    {
        ::operator delete(deadObject);
        return;
    }
    AirPlane* carcass = 
        static_cast<Airplane*>(deadObject);
    carass->next = headOfFreeList;
    headOfFreelist = carass;

}

static allocator

上面的做法需要对每一个class重写一个member operator new 和member operator delete
解决方法将之包装成一个memory allocator 重复使用。

class allocator
{
private:
    struct obj{
        struct obj* next;//embeded pointer
    };
public:
    void* allocate(size_t);
    void deallocate(void*,size_t)
private:
        obj* freeStore = nullptr;
        const int CHUNK = 5;
};
void allocatorallocate(size_t size){
    obj* p;
    if(freeStore){
        //linked list 为空申请一块大内存
        size_t chunk = CHUNK*size;
        freeStore = p = (obj*)malloc(chunk);
        //将大块分为小块串起来
        for(int i = 0; i < CHUNK - 1; ++i){
            p->next = (obj*)((char*)p + size);
            p = p->next;
        }
        p->next = nullptr;
    }
    p = freeStore;
    freeStore = freeStore->next;
    return p; 
}
void allocator:: deallocate(void* p,size_t){
    ((obj*)p)-> next = freeStore;
    freeStore = (obj*)p;
}

使用

class Foo{
private:
    long l;
    string str;
    static allocator myAlloc;
public:
    Foo(long l)l(l){};
    static void* operator new(size_t size){
        return myAlloc.allocate(size);
    }
    static void* operator delete(void* phead,size_t size){
        return myAlloc.dealloacte(phead,size);
    }
};

注意的一点是staic operator new/new[] 可以是=delete但是不能=default

std::alloc

vc6 和GNU2.9 allocator 实现

template<class _Ty>
class allocator {
public:
    typedef _SIZT size_type;
    typedef _PDFT diffeence_type;
    typedef _Ty _FARQ *pointer;
    typedef _Ty value_type;
    pointer allocate(size_type _N, const void*){
        return (_Allocate((diffeence_type) _N,(pointer)0));
    }
    void deallocate(void _FARQ * _P, size_type)
        {operator delete(_P);}
};
template<class _Ty> inline
_Ty FARQ * _Allocate(_PDFT _N, _Ty _FARQ*){
    if(_N<0) _N = 0;
    return ((_Ty _FARQ*) operator new((SIZT)_N* sizeof(_Ty)));
}

使用

template<class _Ty,class _A = allocator<_Ty>>
class vector{...};

std::alloc_pool

freelist 有16个node每个node代表可以存放的内存块大小最小为4 Byte 依次*2增长。示例:
分配一个32 的内存, 先检查 freelist[3] 是否有空余块,如果有则返回,如果没有则malloc 132*20*2 +RoundUp() = 1280大小内存, 20*2 为固定值,用来多申请一些备用, RoundUp() 每次申请在同一node 上都会增大,并且还用来调整内存对齐。这1280种640 拿来切片成20 个放在freelist[3]上,剩下640备用。当其他node也有内存申请时 这些内存则连接到其他node上使用。但是,当,其他node需要内存分配但是备用的内存不够那个node 所需的一个空间时,则就近向freelist 头搜索到最近的node 将剩余备用内存切片并挂到那个node上,然后再在所需的node上malloc申请内存。当全部内存申请完不够alloc时比如需要80个byte时,则就近搜索那个node上面的内存剩余够用将其挂在80那个node上供其使用。如果还是找不到,则折半需求变为40看系统有没有空间,如果没有再折半最终获取到所需要的内存。

具体实现待补充。。。。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值