首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > C++ >

满载new和delete方法实现C++内存安全

2012-11-06 
重载new和delete方法实现C++内存安全    C使用new关键字创建的对象,被分配到堆内存空间,然后得到对象地址,

重载new和delete方法实现C++内存安全

    C++使用new关键字创建的对象,被分配到堆内存空间,然后得到对象地址,当程序复杂庞大时容易发生访问地址bug或内存泄露bug。为了避免内存泄露并在调试程序时找到内存泄露的bug,可以重载new和delete函数,确保程序的内存安全。

    new和delete关键字都可以作为operator来重载。重载的new函数规定一个参数size_t表示需要在堆空间上分配的内存大小,可以认为等于sizeof得到的大小,这个数值由系统创建对象时传递来。一般的实现方法就是:

#include <stdlib.h>void* operator new(size_t type_size){  return malloc(type_size);}

    重载的delete函数规定一个参数void*表示要删除的对象地址,一般的实现方法就是:

#include <stdlib.h>void operator delete(void* obj_ptr){    free(obj_ptr);}

    在这两个函数里加入一些调试代码,监视对象的创建和删除,就能比较容易找到bug位置。对于在函数堆栈上按值创建的对象则无需关心,因为函数在堆栈上创建和删除对象不会调用重载的new和delete方法。

    对于查找内存泄露bug,这样做已经可以解决问题,然而我们可以做的更完美一些。考虑一下COM和类似的接口系统,为了正确的引用接口,调用者必须放弃使用new和delete,通过接口的AddRef和Release实现内存安全。然而这样带来的负面影响很大,就是接口必须由实现者完整实现,一旦底层的接口实现被封装,接口类就无法当做普通类来继承,失去了面向对象编程的特性。而基于重载new和delete,我们可以实现一个既可以用new创建,delete删除,又可以用AddRef()引用,Release()释放的类,这个类的基本功能封装之后,可以当做普通的类被继承,而它和继承类都具有以下特性:

    1. 创建者只关心用new来创建,用delete删除,就能保证对象内存安全。

    2. 使用者只需要用AddRef引用它,用Release删除它,也能保证对象内存安全。

    3. 这两种使用方式同时存在仍然可以保证内存安全。

    这个类可以视为一种引用类型,所以命名为reference_type,通过重载new和delete,加上引用计数方法,最终得以实现。

#ifndef __REFERENCE_TYPE_HPP#define __REFERENCE_TYPE_HPP#include<exception>#include<stdlib.h>struct __declspec(novtable) reference_type{private:    __int64 __ref_count;public:    reference_type(): __ref_count(1) { }    virtual ~reference_type()    {        this->__ref_count--;    }    void _hold()    {        this->__ref_count++;    }    void _drop()    {        this->__ref_count--;        if(this->__ref_count <= 0)        {            delete this;        }    }    void* operator new(size_t obj_size)    {        return malloc(obj_size);    }    void* operator new[](size_t arr_size) throw(std::bad_alloc)    {        throw std::bad_alloc();    }    void operator delete(void* obj_addr)    {        reference_type* obj_t = (reference_type*)obj_addr;        if(obj_t->__ref_count <= 0)        {            free(obj_addr);        }    }    void operator delete[](void* arr_addr) throw(std::bad_alloc)    {        throw std::bad_alloc();    }};#endif

    这个对象可以被new和delete,同时也可以被引用和释放,_hold()相当于COM里的AddRef(),_drop()相当于COM里的Release()。实现的关键在于重载的new和delete也实现了引用计数功能。当delete函数使用了引用计数时,对象不是一定会被删除,而是直到引用计数归零时被删除,这样就实现了内存安全。最重要的是,这个类可以被继承了,保留面向对象编程的特性。

    对象一旦具有安全引用的功能,就不能按值创建在函数堆栈上了,一旦函数退出自动清掉堆栈,引用就失效,再引用或释放对象就把程序搞崩溃。还有一个新的限制就是,不能创建对象数组!数组中的所有对象值是分配在一个连续的空间,只能一起创建并一起删除,此时引用计数同样全部失效。比如,引用数组某个成员后又用delete[]删除数组,其实被引用的成员也一起删除了,而程序还认为它处于引用状态并访问,最后同样把程序搞崩溃。代码中为了防止这个严重的情况发生,已经重载了new[]和delete[],并直接抛出异常。如果需要创建数组,需要实现一个引用安全的数组模板类,这已经跟C#和CLI等高级语言相似。

    总的来说,对于reference_type和它的派生类,它们的优点就是使代码规范并让程序内存变安全,而以下列出一些缺点和使用限制,需要在应用中注意:

    1. 不能在函数里创建对象值了,只能用new和delete;

    2. 不能创建为对象数组,如果需要使用数组,只能创建为对象指针数组,或者实现一个引用安全的数组模板;

    3. 确保_hold()和_drop()成对使用(当然new和delete也是永远需要成对使用),对象内存就可以安全;

    4. 引用计数功能有极小的性能下降,但多数情况下可以忽略不计;

    5. 这个方法仅限于C++面向对象编程,如果程序分配内存是直接使用C语言的malloc(),realloc(),free(),则需要其它方法,比如给malloc增加hook函数。

热点排行