重载new和delete运算符

内存管理运算符 new、new[]、delete 和 delete[] 也可以进行重载,其重载形式既可以是类的成员函数,也可以是全局函数。一般情况下,内建的内存管理运算符就够用了,只有在需要自己管理内存时才会重载。

以成员函数的形式重载 new 运算符:

void * className::operator new( size_t size ){
    //TODO:
}

以全局函数的形式重载 new 运算符:

void * operator new( size_t size ){
    //TODO:
}

两种重载形式的返回值相同,都是void *类型,并且都有一个参数,为size_t类型

在重载 new 或 new[] 时,无论是作为成员函数还是作为全局函数,它的第一个参数必须是 size_t 类型size_t 表示的是要分配空间的大小对于 new[] 的重载函数而言,size_t 则表示所需要分配的所有空间的总和。

重载的new必须有一个size_t参数。这个参数由编译器产生并传递给我们它是要分配内存的对象的长度。必须返回一个指向等于这个长度(或者大于这个长度)的对象的指针。如果没有找到存储单元(在这种情况下,构造函数不被调用),则返回一个0。然后如果找不到存储单元,不能仅仅返回0,我们还应该调用new-handler或产生一个异常信息之类的事,告诉这里出现了问题。

我们首先需要明白的一点就是:operator new()的返回值是一个void*,而不是指向任何特定类型的指针。所做的是分配内存,而不是完成一个对象建立--直到构造函数调用了才完成对象的创建,它是编译器确保做的动作,不在我们的控制范围之内了,所以我们就没有必要考虑。

size_t 在头文件 <cstdio> 中被定义为typedef unsigned int size_t;,也就是无符号整型。

当然,重载函数也可以有其他参数,但都必须有默认值,并且第一个参数的类型必须是 size_t。

同样的,delete 运算符也有两种重载形式。以类的成员函数的形式进行重载:

void className::operator delete( void *ptr){
    //TODO:
}

以全局函数的形式进行重载:

void operator delete( void *ptr){
    //TODO:
}

两种重载形式的返回值都是 void 类型,并且都必须有一个 void 类型的指针作为参数,该指针指向需要释放的内存空间

当我们以类的成员函数的形式重载了new 和 delete 操作符,其使用方法如下:

C * c = new C; //分配内存空间
//TODO:
delete c; //释放内存空间

如果类中没有定义 new 和 delete 的重载函数,那么会自动调用内建的 new 和 delete 运算符。

重载全局new和delete

#include <QCoreApplication>

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#define inf 0x7fffffff
using namespace std;

void* operator new(size_t sz)
{
    printf("operator new: %d Bytes\n",sz);
    void* m = malloc(sz);
    if (!m) puts("out of memory");
    return m;
}

void* operator new [](size_t sz)
{
    printf("operator new: %d Bytes\n",sz);
    void* m = malloc(sz);
    if (!m) puts("out of memory");
    return m;
}

void operator delete(void* m)
{
    puts("operator delete");
    free(m);
}

void operator delete[](void* m)
{
    puts("operator delete");
    free(m);
}

class S
{
public:
    S() {puts("S::S()"); }
    ~S() {puts("S::~S()"); }
private:
    int an[1000];
};


void func()
{

    puts("creating & destroying an int");
    int* q = new int(23);
    delete q;
    puts("creating & destroying an int[]");
    int* p = new int[10]();
    delete []p;
    puts("creating & destroying an s");
    S* s = new S;
    delete s;
    puts("creating & destroying S[3]");
    S* sa = new S[3];
    delete []sa;
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    func();

    return a.exec();
}

输出:

creating & destroying an int
operator new: 4 Bytes
operator delete
creating & destroying an int[]
operator new: 40 Bytes
operator delete
creating & destroying an s
operator new: 4000 Bytes
S::S()
S::~S()
operator delete
creating & destroying S[3]
operator new: 12004 Bytes
S::S()
S::S()
S::S()
S::~S()
S::~S()
S::~S()
operator delete

 

对于一个类重载new和delete

为一个类重载new和delete的时候,尽管不必显式的使用static,但是实际上仍是在创建static成员函数。它的语法也和重载任何其它运算符一样。当编译器看到使用new创建自己定义的类的对象时,它选择成员版本的operator new()而不是全局版本的new()。但是全局版本的new和delete仍为所有其他类型对象使用(除非它们也有自己的new和delete)。这个和全局变量、局部变量的意思是一样的。

#include<iostream>
#include<cstddef>
#include<fstream>
#include<new>
using namespace std;
ofstream out("Framis.out");

class Framis
{
public:
    enum{psize = 100 };
    Framis() {out<< "Framis()" <<endl; }
    ~Framis() {out<< "~Framis() ... " <<endl; }
    void* operator new(size_t) throw (bad_alloc);
    void operator delete(void*);
private:
    enum{sz = 10 };
    char c[sz];
    static unsigned char pool[];
    static bool alloc_map[];
};
unsigned char Framis::pool[psize*sizeof(Framis)];
bool Framis::alloc_map[psize]={false};

void* Framis::operator new(size_t sz) throw(bad_alloc)
{
    for (int i=0; i<psize; ++i) {
        if (!alloc_map[i]) {
            out<< "using block " << i << " ... ";
            alloc_map[i]=true;
            return pool+(i*sizeof(Framis));
        }
    }
    out<< "out of memory" <<endl;
    throw bad_alloc();
}

void Framis::operator delete(void* m)
{
    if (!m) return;
    unsigned long block = (unsigned long)m-(unsigned long)pool;
    block /= sizeof(Framis);
    out<< "freeing block " << block <<endl;
    alloc_map[block]=false;
}

int main()
{
    Framis* f[Framis::psize];
    try {
        for (int i=0; i<Framis::psize; i++) {
            f[i]=new Framis;
        }
        new Framis;
    } catch(bad_alloc) {
        cerr<< "Out of memory!" <<endl;
    }
    delete f[10];
    f[10]=0;
    Framis* X=new Framis;
    delete X;
    for (int j=0; j<Framis::psize; j++) {
        delete f[j];
    }
    return 0;
}

 

为数组重载new和delete

上一段文字中我们讲到如果为一个类重载operator new()和operator delete(),那么无论何时创建这个类的一个对象都将调用这些运算符。但是如果要创建这个类的一个对象数组的时候,全局operator new()就会被立即调用,用来为这个数组分配足够的内存。对此,我们可以通过为这个类重载运算符的数组版本,即operator new[]和operator delete[],来控制对象数组的内存分配。

#include<iostream>
#include<fstream>
#include<new>
using namespace std;
ofstream trace("ArrayOperatorNew.out");

class Widget
{
public:
    Widget() {trace<< "*" <<endl; }
    ~Widget() {trace<< "~" <<endl; }
    void* operator new(size_t sz) {
        trace<< "Widget::new: " << sz << " byte" <<endl;
        return ::new char[sz];
    }
    void operator delete(void* p) {
        trace<< "Widget::delete" <<endl;
        ::delete []p;
    }
    void* operator new[](size_t sz) {
        trace<< "Widget::new[]: " << sz << " bytes" <<endl;
        return ::new char[sz];
    }
    void operator delete[](void* p) {
        trace<< "Widget::delete[]" <<endl;
        ::delete []p;
    }
private:
    enum{sz=10 };
    int an[sz];
};

int main()
{
    trace<< "new Widget" <<endl;
    Widget* w=new Widget;
    trace<<endl<< "delete Widget" <<endl;
    delete w;
    trace<<endl<< "new Widget[25]" <<endl;
    Widget* wa=new Widget[25];
    trace<<endl<< "delete []Widget" <<endl;
    delete []wa;
    return 0;
}

C++空类的大小是1

猜你喜欢

转载自www.cnblogs.com/xiangtingshen/p/10970903.html
今日推荐