【Exceptional C++(16)】Fast Pimpl技
2018-01-30 本文已影响23人
downdemo
- malloc()和new()调用开销很大,下面代码最初在class Y中设计了一个类型X的成员
// file y.h
#include "x.h"
class Y {
...
X x_;
};
// file y.cpp
Y::Y() {}
- 这个class Y的声明需要X的声明可见(从x.h中),为避免这个条件改写为
// file y.h
class X;
class Y {
...
X* px_;
};
// file y.cpp
#include "x.h"
Y::Y() : px_(new X) {}
Y::~Y() { delete px_; px_ = 0; }
- 这很好地隐藏了X,但Y被广泛使用时动态内存分配开销降低了性能。隐藏X的动机是避免Y的用户必须知道X,为了消除这种依赖,通常使用pimp方法,但pimpl由于为X的对象分配内存也会导致性能下降。解决方法是Fast Pimpl,使用一个高效的Pimpl,也就是有自己的operator new的pimpl类
// file y.h
class YImpl;
class Y {
...
YImpl* pimpl_;
};
// file y.cpp
#include "x.h"
struct YImpl {
... // 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(...);
void Deallocate(void*);
private:
// implemented using static?
};
- 因为私有细节很可能用static实现,因此问题是Deallocate是否被一个static对象的析构函数调用,也许更安全的方法是使用single模式,为每个被请求的尺寸使用一个独立链表,并用一个唯一对象管理这些链表
class FixedAllocator {
public:
static FixedAllocator* Instance();
void* Allocate(size_t);
void Deallocate(void*);
private:
// singleton implementation
};
// 用一个辅助基类封装调用
struct FastPimpl {
void* operator new(size_t s) {
return FixedAllocator::Instance()->Allocate(s);
}
void operator delete(void* p) {
FixedAllocator::Instance()->Deallocate(p);
}
};
// 现在可以很容易地写出任意的Fast Pimpl
struct YImpl : FastPimpl {
... // private stuff here
};
- 但不要乱用Fast Pimpl,虽然得到了最佳内存分配速度,但维护这些独立链表将导致空间性能小将,因为这会有更多内存碎片,只有在使用了profiler剖析性能并证明需要性能优化后才使用此方法