关闭

重载new和delete的一些规则

标签: c语言c++newdelete重载
950人阅读 评论(0) 收藏 举报
分类:

重载new和delete的一些规则

参考:《Effective C++》

一般规则

1.内存分配失败时调用new_handler
关于new_handler参考:
http://blog.csdn.net/sanoseiichirou/article/details/49945791
2.处理0内存的情况(一种处理方案是将0字节内存请求当作1字节来分配)

演示

#include <iostream>
#include <cstdlib>

void *operator new(size_t size){
    std::cout << "Operator new called" << std::endl;
    if (size == 0)size = 1;//处理0字节情况
    void *pmem(nullptr);//保存分配好的内存首地址
    std::new_handler handler;//保存new_handler
    while (true) {
        pmem = malloc(size);
        if (pmem)return pmem;

        //下面的两句由于获取new_heandler,因为没有直接的方法可以获取new_handler
        //当然这里针对的是单线程的情况,如果是多线程,就需要LOCK了
        handler = std::set_new_handler(nullptr);
        std::set_new_handler(handler);

        //如果new_handler不为空,就调用它,如果为空,直接抛出异常
        if (handler)handler();
        else throw(std::bad_alloc());
    };
}

void OutOfMem() {
    std::cout << "Run out of Mem" << std::endl;
    abort();
}

int main() {
    std::set_new_handler(OutOfMem);
    int *p(new int(243));
    return 0;
}

从上面的代码我们观察到:只有几种方法可以终结new_handler的调用:
1.new分配内存成功并返回
2.new_handler为空,直接抛出std::bad_alloc异常
3.new_handler所指向的函数中包含abort()或exit()等直接终止程序运行的函数。
4.其他……

特殊规则1

*重载new和delete的一个重要原因是要优化某个对象的内存分配。为了安全,我们需要检测分配的大小是否正确。
要注意的是:重载的局部new和delete都是为本类服务的,而不是本类的派生类
如:

#include <iostream>
#include <cstdlib>

class base{
public:
    base():dat(100){}
    void *operator new(size_t size){
        std::cout<<"base operator new"<<std::endl;
        return malloc(size);
    }
private:
    int dat;
};

class derived:public base{
public:
    derived():datx(200){}
private:
    int datx;
};

int main(){
    derived *p(new derived);
    return 0;
}

这里写图片描述
如图:此时调用的是基类的operator new
但是我们都知道,一般派生类比基类要大。

解决方法

#include <iostream>
#include <new>
#include <cstdlib>

class base{
public:
    base():dat(0){}
    void *operator new(std::size_t size){
        std::cout<<"base operator new called - size="<<size<<std::endl;//调试用的语句
        if(size!=sizeof(base)){ //如果调用new的是派生类,则跳转到全局的new
            std::cout<<"turn to global new for help"<<std::endl;//调试用的语句
            return ::operator new(size);
        }
        return malloc(size);
    }
    void operator delete(void *p,std::size_t size){
        std::cout<<"base operator delete called - size="<<size<<std::endl;//调试用的语句
        if(p){  //检测p是否为nullptr,遵守C++释放空指针永远不报错的规则
            if(size!=sizeof(base)){  //如果调用new的是派生类,则跳转到全局的delete
                std::cout<<"turn to global delete for help"<<std::endl;//调试用的语句
                return ::operator delete(p);
            }
            delete p;
        }
    }
private:
    int dat;
};

class myclass:public base{
public:
    myclass():datx(0){}
private:
    int datx;
};

int main(){
    base *p(new base);
    delete p;
    std::cout<<'\n';
    myclass *px(new myclass);
    delete px;
    return 0;
}

这里写图片描述
如图:我们成功地将重任交给了global new

当然,delete同样也要检测,若大小不符,就要交给global delete来处理。

疑问:为什么我们这里不用检测分配0字节内存的情况呢?
看个例子:
这里写图片描述
现在知道为啥不用检测分配0字节的情况了吧。

特殊规则2

*array new也就是类似void *operator new[](size_t size)的new版本,做法就是直接分配size大小一块内存即可。
理由是:我们无法确定元素的大小和要分配的个数。(如:特殊规则1 中讲述的,对于派生类,我们无法使用基类的operator new为其分配内存;其次,new常常会多分配出一些空间来存储额外的信息)

#include <iostream>
#include <new>
#include <cstdlib>

class demo{
public:
    demo():dat(0){
        std::cout<<"demo constructor called"<<std::endl;
    }
    ~demo(){
        std::cout<<"demo destructor called"<<std::endl;
    }
    void *operator new(std::size_t size){
        if(size!=sizeof(demo))return ::operator new(size);//如果分配的大小不符,转交给全局new处理
        void *p(nullptr);
        std::new_handler handler(std::set_new_handler(nullptr));
        std::set_new_handler(handler);
        while(true){
            if(p=malloc(size))return p;
            if(handler)handler();
            else throw std::bad_alloc();
        }
    }
    void operator delete(void *p,std::size_t size){
        if(p){
            if(size!=sizeof(demo))return ::operator delete(p);//如果大小不符,转交给全局delete处理
            free(p);
        }
    }
    void *operator new[](std::size_t size){
        //直接将责任交给demo中的operator new
        //而operator new直接mallo返回一片raw memory(没有经过任何操作的一块内存)
        return operator new(size);
        //当然也可以直接malloc
        //return malloc(size);
    }
    void operator delete[](void *p){
        if(p)free(p);
    }
private:
    int dat;
};

int main(){
    demo *p=new demo;
    delete p;
    std::cout<<"\n\n";
    demo *px(new demo[3]);
    delete [] px;
    return 0;
}

这里写图片描述

特殊规则3

*删除nullptr是安全的

#include <iostream>
#include <cstdlib>

class demo{
public:
    demo():dat(100){}
    void *operator new(size_t size)throw(std::bad_alloc){
        return malloc(size);
    }
    void operator delete(void *p)throw(){
        if(p!=0)delete p;
    }
private:
    int dat;
};

int main(){
    return 0;
}

new和delete重载实例

#include <iostream>
#include <cstdlib>

class demo{
public:
    demo(){std::cout<<"Default Constructor"<<std::endl;}
    demo(int dat):m_dat(dat){std::cout<<"Unary Constructor"<<std::endl;}
    ~demo(){std::cout<<"Default Destructor"<<std::endl;}
    void * operator new(size_t size)throw(std::bad_alloc){
        std::cout<<"operator new"<<std::endl;
        if(size!=sizeof(demo))return ::operator new(size);
        void *pmem(nullptr);
        std::new_handler handler(nullptr);
        while(true){
            if(pmem=malloc(size))return pmem;
            handler=std::set_new_handler(nullptr);
            std::set_new_handler(handler);
            if(handler)handler();
            else throw(std::bad_alloc());
        }
    }
    void *operator new[](size_t size){
        std::cout<<"operator new[]"<<std::endl;
        return operator new(size);
    }
    void operator delete(void *p){
        std::cout<<"operator delete"<<std::endl;
        if(p)free(p);
    }
    void operator delete[](void *p){
        std::cout<<"operator delete[]"<<std::endl;
        operator delete(p);
    }
private:
    int m_dat=0;
};

int main(){
    demo *p(new demo);
    delete p;
    std::cout<<"\n\n";
    p=new demo[3];
    delete []p;
    return 0;
}

这里写图片描述

直接获取当前的new_handler

刚才已经说过,我们没有方法直接获取new_handler,没错,但是,在VC++的最新库中已经包含有 get_new_handler 。位于< new > 头文件中。
看图:
这里写图片描述
下面是实例:

#include <iostream>
#include <new>

int main() {
    std::new_handler h(std::get_new_handler());
    std::cout << h << std::endl;
    std::cin.get();
    return 0;
}

这里写图片描述

**转载请注明出处

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:20733次
    • 积分:439
    • 等级:
    • 排名:千里之外
    • 原创:20篇
    • 转载:1篇
    • 译文:0篇
    • 评论:0条