• 特定于类的内存管理---《C++必知必会》 条款36


    我们可以量身定制 operator new 和 operator delete 用于某个类类型,而不是必须使用标准版的 operator new 和 operator delete。

    注意:我们不可以对 new  和 delete 操作符做什么,它们的行为是固定的。但是我们可以改变 它们所调用的 operator new 和 operator delete 。做这件事的最佳方式是为类声明 operator new 和 operator delete成员函数:

    class Handle
    {
    public:
        //...
        void * operator new (size_t);
        void operator delete(void *);
    };
    //...
    Handle * h = new Handle;//使用Handle::operator new
    //...
    delete h;//使用Handle::operator delete

    在一个new表达式中分配一个类型为 Handle 的对象时,编译器首先会在Handle 的作用域内查找一个 operator new ,如果没有找到,它将会使用全局作用域中的 operator new。 operator delete 情况是类似的。因此通常来说,如果定义一个operator new ,最好同时也定义一个成员 operator delete   ,反之亦然。

    成员 operator new 和 operator delete 是静态成员函数,这是有道理的。更确切的说,成员 operator new   和 operator delete 之所以是静态成员,是因为前者被调用于类对象构造之前,后者被调用与类对象析构之后,在这两种情况下,对象均不处于有效状态,因此也就无 this 指针可用。我们可以回想起静态成员函数没有 this 指针。由于这个函数仅仅负责获取和释放对象的存储区,因此它们用不着 this 指针。 它们可以被派生类继承:

    class MyHandle:public Handle
    {
    public:
        //...
    };
    //... MyHandle
    * myH = new MyHandle; //使用Handle::operator new //... delete myH; //使用Handle::operator delete

    当然,如果 MyHandle 已经声明了它自己的 operator new 和 operator delete 成员,那么编译器将在查找期间首先发现并采用它们,而不再使用从基类 Handle 继承来的那个版本。

    如果在基类的中了成员 operator new  和 operator delete ,要确保基类析构函数是虚拟的:

    class Handle
    {
    public:
        //...
        virtual ~Handle();
        void * operator new (size_t);
        void operator delete(void *);
    };
    
    class MyHandle:public Handle
    {
    public:
        //...
        void * operator new (size_t);
        void operator delete(void *,size_t);//注意第二个参数
    };

    MyHandle * myH = new MyHandle; //使用MyHandle::operator new
    //...
    delete myH; //使用 MyHandle::operator delete

    对于

    void operator delete(void * ,size_t);而言,第二个类型为size_t的参数有编译器自动初始化为第一个参数所致对象的大小,以字节为单位。

    如果基类析构函数不是虚拟的,那么通过一个基类指针来删除一个派生类对象结果就是未定义的!一个可能简单地实现(并且很可能不是正确地)调用 Handle::operator delete 而不是MyHandle::operator delete,但这样做发生任何事情都是可能的。还要注意的是,我们使用了一个带有两个参数的operator delete 而不是普通的单参数版本的operator delete。不过对于期望派生类来继承其operator delete 实现的基类而言,这不过是另一个“普通”版本的成员 operator delete 而已。第二参数用于保存正在被删除的对象大小,这种信息在实现自定义内存管理时往往很有用。

    一个常见的误解是以为使用new和delete 操作符就意味着使用堆(或自由存储区)内存,其实并非如此。使用new 操作符的唯一暗示是名为 operator new 的函数将被调用,且该函数返回一个指向某块内存饿指针。没错,标准、全局的 operator new 和 operator delete 的确是从堆上分配内存,但是成员 operator new 和 operator delete可以做它自己想做的任何事情。对于分配的内存到底从哪来没有任何限制:它可能来自于一个特殊的堆,也可能来自一个静态分配的块,也可能来自一个标准的容器内部,也可能来自某个函数范围的局部存储区。要说对于这个内存从南来确实存在什么限制因素的话,那就是你创造力和判断力了。例如,Handle对象可以从一个静态块 (static lock) 上进行分配,如下:

    struct rep
    {
        enum{ max = 1000 };
        static rep * free; //“自由”列表 (freeList)的头部
        static int num_used; //被使用的槽位(slot)数目
        union
        {
            char store[sizeof(Handle)];
            rep * next;
        };
    };
     int rep::num_used =0;
     rep* rep::free = NULL;
    static rep mem[ rep::max ]; //静态存储块e
    void * Handle::operator new(size_t size)
    {
        if(rep::free) //如果freelist上有一些东西
        {
            rep * tmp = rep::free; //从freeList取出
            rep::free = rep::free->next;
            return tmp;
        }
        else if( rep::num_used < rep::max )//如果没有剩余的槽位
        {
            return &mem[ rep::num_used++ ]; //返回未被使用的槽位
        }
        else //否则,我们现在所处的状况是···
            throw std::bad_alloc();//没有可用内存了
    }
    void Handle::operator delete(void * p)
    {
        static_cast<rep*>(p)->next = rep::free;
        rep::free =  static_cast<rep *>(p);
    } 

    对于实现的一个“产品质量”的版本更要小心处理内存不足的情况,应该提供更强健的处理,并且要处理Handle的派生类类型以及Handle数组的情况,等等。不过这段简单的代码已经表明new 和 delete 不必非使用堆内存不可。

    此章节代码,我上机测试过,没有问题

    啊 。。。好东西!!!好文章!!!受益颇深。。。敲的我好累

    2018.3.23我最棒

  • 相关阅读:
    arpg网页游戏之地图(四)
    arpg网页游戏之地图(三)
    arpg网页游戏之地图(二)
    arpg网页游戏之地图(一)
    cocos2dx三种定时器的使用以及停止schedule,scheduleUpdate,scheduleOnce。
    盖茨的几点忠告【转】
    桌面快捷方式图标都有一个蓝色的阴影
    人际关系百条细节
    需求分析的六个原则
    小细节,你做到了么?
  • 原文地址:https://www.cnblogs.com/azbane/p/8632908.html
Copyright © 2020-2023  润新知