c++ new 重载

来源:互联网 发布:网络科学引论 编辑:程序博客网 时间:2024/06/07 02:04
转自:#include <cstdio>#include <cstdlib>void * operator new(size_t unSize){    printf("operator new called\n");    return malloc(unSize);}void * operator new[](size_t unSize){    printf("operator [] called\n");    return malloc(unSize);}void * operator new(size_t unSize, int nLine, const char * pFunc){    printf("operator new called, line: %d, func: %s\n",        nLine, pFunc);    return malloc(unSize);}//注意:c++14才支持全局delete或delete【】有多个参数,参看参考资料2.下面两个delete不会覆盖全局的。void operator delete(void * pMem,size_t unSize){     printf("delete1: %u\n", unSize);    free(pMem);}void operator delete[](void * pMem, size_t unSize){          printf("delete[]: %u\n", unSize);          free(pMem); }class A{public:        A(int a = 0) :      _a(a)      {             printf("constructor called\n");      }       virtual ~A()      {          printf("~A()\n");      }      void * operator new(size_t unSize)      {          printf(" calledA\n");          return malloc(unSize);                }     //注意:但是支持自定义类型操作符new或delete重载支持size_t参数。即要删除对象的大小delete,要删除对象数组大小delete[].。      void operator delete(void * pMem, size_t unSize)      {          printf("delete2: %u\n", unSize);          free(pMem);      }      void operator delete[](void * pMem, size_t unSize)      {          printf("delete[]: %u\n", unSize);          free(pMem);      }      private:        int _a;};class B: public A{public:    //隐式的为静态函数。    void * operator new(size_t unSize, int nLine, const char * pFunc)    {        printf("operator new called, line: %d, fileB: %s\n",            nLine, pFunc);        printf("operator new: %u\n", unSize);        //_b=0;        return malloc(unSize);    }        ~B()    {        printf("~B()\n");    }        int _b;        int _bb;};int main(){    A * pA = new A(10);    printf("#######\n");        A * pB = new (__LINE__, __FILE__) B();    printf("#######\n");        A * szA = new A[10];    B *szB = new B[10];    printf("#######\n");        delete pA;     printf("#######\n");        delete pB;     printf("#######\n");        delete [] szA;    printf("#######\n");        delete [] szB;    printf("#######\n");//下面两个不是自定义类,没有类重载new.delete故只能调用全局的,本程序全局不支持size_t参数,故只能调用标准C++中的全局operate delete.故不会打印信息。    char * pC = new char[10];    delete [] pC;     char *pu = NULL;    delete pu;}
复制代码

gcc下运行结果:

复制代码
 calledAconstructor called#######operator new called,operator new: 16constructor called#######operator [] calledconstructor calledconstructor calledconstructor calledconstructor calledconstructor calledconstructor calledconstructor calledconstructor calledconstructor calledconstructor calledoperator [] calledconstructor calledconstructor calledconstructor calledconstructor calledconstructor calledconstructor calledconstructor calledconstructor calledconstructor calledconstructor called#######~A()delete2: 8#######~B()~A()delete2: 16#######~A()~A()~A()~A()~A()~A()~A()~A()~A()~A()delete[]: 84//注意84=4+8*10,数组分配的堆空间,第一个int空间放数组个数,接下来顺序放对象#######~B()~A()~B()~A()~B()~A()~B()~A()~B()~A()~B()~A()~B()~A()~B()~A()~B()~A()~B()~A()delete[]: 164  #######operator [] called //调用覆盖的全局函数operate new [],打印信息。                    //delete[]调用全局的。
复制代码

 说明:1 在vs中类中delete[]的size_t是8和16.不同编译器结果不同。理论上应该是数组大小*类型字节。

         2 如果基类有virtual析构函数,则传给operator delete的大小讲个怒被删除指针所指对象的动态类型而变化,如果没有声明为virtual,那么通过基类指针删除指向派生类对象,大小为基类大小。

总结:

1 malloc只是C函数,内部应该是使用类似deepalloc等,主要功能是分配原始内存,返回void*类型,需要显示转换为对应对象 类型指针。没有调用对象构造函数。C中函数本来就没有类没有构造函数。

   free释放回收堆空间。

2 C++中的new 和delete是操作符,A * pA = new A(10)分别调用下面两步:

先调用void * operator new(size_t unSize) (调用顺序:那个有就调用那个:类中重载了就调用类的,然后全局重载,最后是C++源码中的全局函数)分配原始内存,未初始化的。里面可以调用malloc或者类似deapalloc来分配内存。

在调用A的构造函数,用初始化参数初始化。

delete pA;也分两步:

先调用A析构函数;

在调用voidoperator delete(void * pMem)(调用顺序同上面operator new),释放内存。可调用free实现或者其他实现。

3 operator new,operator delete隐式静态函数,写不写static都是静态的。

为什么必须是静态的呢?因为他们要么在构造对象前使用要么在析构后使用。属于类的函数,不是对象的函数。

4 operator new中传入的大小是怎么获得的呢,我觉得类似于sizeof(类型名)

关于sizeof不需要实例对象,我觉得类为实例化编译器不会给成员变量分配空间,但应该有个地方放的声明,标明变量类型等。

还有,sizeof大小不包括静态成员变量,静态变量一般放在静态区,不属于对象。没有成员变量的类大小为1,有虚函数就要多4个字节(虚函数表指针)。

 

最后小结一下,new 和delete都是内建的操作符,语言本身所固定了,无法重新定制。但它所调用的内存分配/释放的函数,即operator new和operator delete可以被重载

ps:

从汇编代码可看出:对单个堆对象调用delete和对堆对象数组调用delete[]可以发现,两种情况最主要的差别是在调用析构代理函数时传递的对象首地址,用delete析构单个对象是传进的堆空间首地址(也是堆对象首地址),而用delete[]析构对象数组时传递的是偏移堆空间首地址4byte处内存地址(即堆空间中第一个对象首地址,前四个字节是个int型,放的数组大小,即对象个数)。因此,在释放单个对象时调用delete[],或者释放对象数组时调用delete,都会造成错误,应该配对使用。


#include<iostream>
#include <cstdlib>
using namespacestd;

/* 重载new应返回void*类型,如果内存分配请求成功,就返回指向内存的指针;
 * 如果失败,则遵循规定抛出一个std::bad_alloc类型的异常
 * 重载operator new需要注意的一些问题,参见:
 * http://blog.csdn.net/xushiweizh/archive/2006/11/19/1395783.aspx
 * 重载delete以void*为参数,
 */


/* 重载了全局的new操作符 */
void* operator new (unsigned int size)
{
    cout <<"::new" <<endl;
    cout << size<< endl;
    if(!size)
        size = 1;
    void *mem= malloc(size);
    cout << mem<< endl;
    return mem;
}

/* 重载了全局的delete操作符 */
void operatordelete (void*ptr)
{
    cout <<"::delete" <<endl;
    cout << ptr<< endl;
    if(!ptr)
        return;
    free(ptr);
}

class Point {
public:
    Point(int x= 1, int y = 1)
    {
        this->x= x;
        this->y= y;
    }
    ~Point(){};

    /* 重载类Point的new操作符 */
    void* operator new (unsigned int size)
    {
        /* Point类可能会被继承,派生类使用继承的new
         * 可能导致错误,将这些情况交给全局的new处理
         */

        if(size!= sizeof(Point))
            return ::operatornew(size);

        cout <<"Point::new" <<endl;
        cout << size<< endl;
        if(!size)
            size = 1;
        void *mem= malloc(size);
        cout << mem<< endl;
        return mem;
    }

    /* 重载类Point的delete操作符 */
    void operatordelete (void*ptr)
    {
        /* 对于空指针,不进行处理 */
        if(ptr== NULL)
            return;

        cout <<"Point::delete" <<endl;
        cout << ptr<< endl;
        if(!ptr)
            return;
        free(ptr);
    }

    /* 重载类Point的new[]操作符 */
    void* operator new[](unsigned int size)
    {
        cout <<"Point::new" <<endl;
        cout << size<< endl;
        if(!size)
            size = 1;
        void *mem= malloc(size);
        cout << mem<< endl;
        return mem;
    }

    /* 重载类Point的delete[]操作符 */
    void operatordelete[](void *ptr)
    {
        cout <<"Point::delete" <<endl;
        cout << ptr<< endl;
        if(!ptr)
            return;
        free(ptr);
    }

    /* 重载<<操作符 */
    friend ostream&operator <<(ostream& s, Point& p);
private:
    int x;
    int y;
};

ostream& operator <<(ostream& s, Point& p)
{
    s << p.x<< " " << p.y;
    return s;
}

int main()
{
    cout <<"sizeof(Point) = " << sizeof(Point)<< endl;

    /* 使用类的new操作符
     * 一次申请一个元素
     * 传入new的size值与实际需要的空间相等
     */

    Point *p =new Point;
    cout << p<< endl;

    cout <<endl <<"---------------------" << endl<< endl;

    /* 一次申请多个元素时
     * 实际传入new的size值比需要的空间多4个字节
     * 这第一个字节用于存储分配的个数
     * 用户实际使用的空间从第二个字节开始
     */

    Point *p2 =new Point[2];
    cout << p2<< endl;
    int *intp= (int*)p2;
    delete p;

    /* 连续的16个字节存储的两个Point
     * 构造Point时默认x、y为1
     * 以下四条语句输出均为1
     */

    cout <<*intp <<endl;
    cout <<*(intp + 1) <<endl;
    cout <<*(intp + 2) <<endl;
    cout <<*(intp + 3) <<endl;

    /* 分配的起始地址前一个字节存储分配个数 */
    cout <<*(intp - 1) <<endl;

    /* 释放p2指向的内存空间,传入地址第一字节为分配的个数(2)
     * 根据申请单个元素空间和申请多个元素空间的不同,故释放
     * 时使用的操作符要与申请时使用的对应
     */

    delete []p2;

    cout <<endl <<"---------------------" << endl<< endl;

    /* 使用重载的全局new 与 delete */
    int *ip= new int;
    delete ip;

    return 0;
}

0 0