重载new和delete的一些规则

原创 2015年11月20日 23:18:14

重载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;
}

这里写图片描述

**转载请注明出处

版权声明:本文为博主原创文章,未经博主允许不得转载。

浅谈 C++ 中的 new/delete 和 new[]/delete[]

在 C++ 中,你也许经常使用 new 和 delete 来动态申请和释放内存,但你可曾想过以下问题呢? new 和 delete 是函数吗?new [] 和 delete [] 又是什么?什么时候...
  • hazir
  • hazir
  • 2014年03月17日 22:49
  • 99781

重载全局new/delete实现内存检测

下面介绍用重载new/delete运算符的方式来实现一个简单的内存泄露检测工具,基本思想是重载全局new/delete运算符,被检测代码调用new和delete运算符时就会调用重载过的operator...
  • hzyong_c
  • hzyong_c
  • 2010年10月18日 17:38
  • 9506

重载new和delete,处理内存溢出

很多时候,我们使用new/delete来分配和释放内存。那么这篇问题主要讲的是,使用new来处理实际编程中可能出现的内存泄漏的问题。奇怪,既然你说delete可以释放内存呢,只要用好这两个函数,保证匹...
  • BBinChina
  • BBinChina
  • 2015年03月12日 21:41
  • 574

控制内存分配----重载new和delete & 定位new表达式

定位new表达式, 重载new和delete, 控制内存分配
  • qianqin_2014
  • qianqin_2014
  • 2016年05月05日 11:17
  • 842

实现new和delete的重载

有两种方式: 1.覆盖默认定义的全局方式 2.只针对一个类的局部方式 new的一般语法形式: void* operator new(size_t size) { //使用new运算符分配s...
  • u012967763
  • u012967763
  • 2015年01月09日 21:55
  • 308

如何重载new和delete函数

在嵌入式系统中使用C++的一个常见问题是内存分配,即对new 和 delete 操作符的失控。   具有讽刺意味的是,问题的根源却是C++对内存的管理非常的容易而且安全。具体地说,当一个对象被消除时...
  • talentluke
  • talentluke
  • 2011年01月05日 10:36
  • 3027

C++ new和delete重载

首先,new和delete是运算符,重载new和delete是可能的。这样做的原因是,有时希望使用某种特殊的动态内存分配方法。例如,可能有些分配子程序,他们的堆已耗尽,自动开始把一个磁盘文件当虚存储使...
  • sctq8888
  • sctq8888
  • 2012年07月21日 13:23
  • 2637

New运算符的重载

首先我们要清楚,为什么我们要重载new,和delete了?这还不是指针造成的,确实指针是一件让人喜欢的东西,用起来如此让人喜欢,让人顺手。然而小程序我们完全可以避免内存泄露问题,大程序就不那么容易了,...
  • ghevinn
  • ghevinn
  • 2014年01月16日 15:32
  • 5900

C++ 内存分配(new,operator new)详解

讲述C++ new关键字和operator new, placement new之间的种种关联,new的底层实现,以及operator new的重载和一些在内存池,STL中的应用。...
  • WUDAIJUN
  • WUDAIJUN
  • 2013年07月09日 14:55
  • 53769

C++的三种new简介及重载局部(类内部)与全局operator new

先简单解释下C++的三种new,由于它们的名字实在是。。我就说的通俗点。1、new运算符(new operator)大哥,我们在代码中直接使用的就是它。它做2件事:调用后两种new申请内存和初始化对象...
  • qq_29227939
  • qq_29227939
  • 2016年06月11日 21:35
  • 3372
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:重载new和delete的一些规则
举报原因:
原因补充:

(最多只允许输入30个字)