`
qiezi
  • 浏览: 491400 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

抛开析构函数

    博客分类:
  • c++
阅读更多
内存管理通常指的是堆上分配的空间,栈上分配虽然高效好用,但一般是固定大小的、不能持久保存的。堆上分配就有了释放的问题,代码中有一些是使用new/malloc来分配空间的,还有一些是使用内存池,如何在释放时区分是必须的。

简单思考以后我感觉首先需要排除delete pObject这种调用,这显然是假定对象是用new分配出来的,或者是对象重载了new/delete操作符。也可以把对象根据分配方式不同放在不同的容器中管理,不过带来了查找上的开销。如果不分类放在容器中,也可以让对象自己知道是如何分配的,再给它一个销毁的函数:

struct Foo {
    int allocMethod;

    void Destroy() {
        switch(allocMethod) {
        case ALLOC_NEW: delete this; break;
        case ALLOC_MALLOC: // placement delete + free break;
        // ... memory pool or object pool
    }
};


这么写会很累,每个对象都要知道太多细节,可以考虑把这部分提出来:

struce DestroyCallback {
    virtual call(void* p) = 0;
};

struct Foo {
    DestroyCallback* destorycb;

    void Destory() {
        if (destroycb)
            destroycb->call(this);
    }
};

struct DeleteCallback : public DestroyCallback {
    virtual void call(void* p) {
        delete static_cast<Foo*>(p);
    }
};

struct PoolFreeCallback : public DestroyCallback {
    Pool<Foo>* pool;

    virtual void call(void* p) {
        pool->Release(static_cast<Foo*>(p));
    }
};


这种方式在分配对象的时候把destroy callback设置好,需要释放时调用Destroy就可以了,如果对象需要在一个流程中传递并且在最后阶段销毁,这种方式很容易解除前后端分配器的耦合。使用这种方式就需要用Destroy来代替其它销毁对象的方式,因为它并不一定真的表示销毁,对于对象池来说,它完成的是对象的归还。考虑到池对象重用时前需要初始化,我给对象加上一个初始化方法。由于Destroy并不能准确表明对象的生成状态,我改用Open/Close来代替,Open表明一个对象的重生,通常用来初始化一些变量的值,并不是取代构造函数,这特别适合一些需要池化的大对象,Close也不是析构,但它也可以真正完成析构调用。对于一些特殊的应用,还可以在Open里面完成构造函数调用,Close里面完成析构函数调用,如果对象的空间是使用malloc/free来管理的,这种方式就显得特别有用。这种方式还有一个额外的好处,很多C++开发者不喜欢使用异常,但构造函数没有返回值,只能使用异常来表示失败,多一个Open方法可以有一个不使用异常来检查错误的方法。
分享到:
评论
5 楼 qiezi 2008-01-24  
才看到上面这个评论。我就是从cppblog搬过来的,不太喜欢富文本。
4 楼 Uranus 2007-12-04  
建议楼主在CPPBLOG上导个博客,那边做CPP的人多
3 楼 qiezi 2007-08-16  
目前是把malloc/free和new/delete也包装成了,DestoryCallback一般只和分配器、对象池配套实现,不会有很多,DestoryCallback也实现成了模板接口,如果是单根类库就这个麻烦了。使用过程中感觉还是很灵活的。
2 楼 iunknown 2007-08-16  
不如再彻底一点,把直接使用 malloc/free, new/delete 的对象也归类为一种 pool 。这样所有的对象都是使用 pool 产生出来的。对于 Open/Close 的操作,就统一为 pool 的 create 和  release 操作。每个对象同样额外带着一个 pool 的指针。即把现在的 DestroyCallback 集成到 pool 中。这样就不再是有多种 DestroyCallback 的子类,而是有多种 pool 的子类。

这种思路在 apache 的 apr 项目中大规模地使用。
1 楼 iunknown 2007-08-16  
1.这种方式带来的问题是有很多小的 class 。如果使用 template ,可以减少一些。但起码对应不同的策略,就要有一种 DestroyCallback 的实现。
2.如果使用了 pool ,要保证 pool 和属于它的对象的生命周期要一致。

不过,考虑到这种方案所针对的问题,这种方案带来的问题比起能解决的问题,还是简化了。

相关推荐

Global site tag (gtag.js) - Google Analytics