The Fast Pimpl Idiom

我们有时会以减少依赖或效率的名义倾向走捷径,但某些时刻这可能不是个好主意。下面有个出色的惯用语法同时兼顾了安全和实现目标。

问题

调用标准malloc() 和 new()代价昂贵。在下面的代码中,class Y中有个类型为X的数据成员

    // file y.h
    #include "x.h"

    class Y {
        /*...*/
        X x_;
    };

    // file y.cpp
    Y::Y() {}
类Y的声明需要类X的声明可见,为了避免这种情况,程序员尝试如下:

    // file y.h
    class X;

    class Y {
        /*...*/
        X* px_;
    };

    // file y.cpp
    #include "y.h"

    Y::Y() : px_( new X ) {}
    Y::~Y() { delete px_; px_ = 0; }
这很好的隐藏了X,但结果是Y被广泛使用,动态分配降低了性能。做后程序员无畏的写下了下面“完美的”代码,既不需要在y.h中包含x.h也没有动态分配的消耗。
    // file y.h
    class Y {
        /*...*/
        static const size_t sizeofx = /*some value*/;
        char x_[sizeofx];
    };

    // file y.cpp
    #include "x.h"

    Y::Y() {
        assert( sizeofx >= sizeof(X) );
        new (&x_[0]) X;
    }

    Y::~Y() {
        (reinterpret_cast<X*>(&x_[0]))->~X();
    }
讨论
解决方法
不要这样做!底线,C++不直接支持不透明类型。程序员想要的是其他东西,即"Fast Pimpl" idiom。
"Fast Pimpl"
隐藏X的目的是避免把X暴露给Y的客户。通常C++消除这种实现依赖的措施是使用 pimpl idiom。即上述第一段代码。
这种情况下唯一的问题是pimpl方法的性能,因为需要在空闲存储器给X分配空间。通常对于特定类,解决方法是重载这个类的operator new,因为分配固定大小的空间比一般的分配子更有效率。
不幸的是,这就假设Y的作者也是X的作者。通常这并不是事实。实际解决之道是使用Efficient Pimpl
    // file y.h
    class YImpl;
    class Y {
      /*...*/
      YImpl* pimpl_;
    };
    // file y.cpp
    #include "x.h"
    struct YImpl {  // yes, 'struct' is allowed :-)
      /*...private stuff here...*/

      void* operator new( size_t )   { /*...*/ }
      void  operator delete( void* ) { /*...*/ }
    };
    Y::Y() : pimpl_( new YImpl ) {}
    Y::~Y() { delete pimpl_; pimpl_ = 0; }
如何实现有效的固定大小的分配函数,就可用性方面,一个技术是使用泛形模板:
    template<size_t S>
    class FixedAllocator {
    public:
      void* Allocate( /*requested size is always S*/ );
      void  Deallocate( void* );
    private:
      /*...implemented using statics?...*/
    };
由于私有部分很可能是static,这就有个问题,如果静态对象的dtor曾经调用过Deallocate()。更安全的做法应该使用singleton来管理请求不同大小的空闲链表。
    class FixedAllocator {
    public:
      static FixedAllocator* Instance();

      void* Allocate( size_t );
      void  Deallocate( void* );
    private:
        /*...singleton implementation, typically
             with easier-to-manage statics than
             the templated alternative above...*/
    };
把调用封装进基类:
    struct FastPimpl {
      void* operator new( size_t s ) {
        return FixedAllocator::Instance()->Allocate(s);
      }
      void operator delete( void* p ) {
        FixedAllocator::Instance()->Deallocate(p);
      }
    };
现在你就可以很容易的写出很多你想要的Fast Pimpls
    //  Want this one to be a Fast Pimpl?
    //  Easy, then just inherit...
    struct YImpl : FastPimpl {
      /*...private stuff here...*/
    };


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值