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

这里写图片描述

**转载请注明出处

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

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

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

C++的new 和 delete 操作符重载。

测试代码: // test.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include #include #include using name...

重载delete时的那点事

    C++里允许用户通过自定义operator new的方式来更改new表达式的行为,这给了程序员定制内存管理方案的自由。但是享受这种自由的时候必须遵守一定的规范,具体可以参见 《Effect...

C++ new和delete重载

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

数据库之【游标使用详解篇】

***************************** 数据库之游标使用详解篇 ***************************** 游标(Cursor):用来查询数据库,...

Java小程序:单循环比赛,总分高者获胜的源代码

java5个人4项单循环比赛,每项决胜者得2分,平分各得一分,败者不得分,总分高着获胜的源代码...

new与delete的重载

new与delete的重载 #include "stdafx.h" #include using namespace std; #include #include //new 和delete...

重载new和delete运算符

注意事项就是: 1. 重载的new运算符, 其第一个参数必须是size_t类型. 2. 重载new运算符, 其返回值类型必须是void* 3. 调用方式为: new(arg list) 正常...

C++ new delete执行时机和重载说明

以下代码取自Thinking C++ #include #include //using namespace std; void* operator new(size_t sz) { print...

cppTest-7.6:重载new&delete

/** * cppTest-7.6:重载new&delete * *1、在类里面定义的重载new、delete只能对该类起作用 *2、new、delete都是类的静态成员,而且是自动成为静态成...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:重载new和delete的一些规则
举报原因:
原因补充:

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