// // (C) Porkolab 2003 // // A.8.3. // // Fast PIMPL: PIPML has some drawbacks, like allocating/deallocating objects in the heap, which could be slow. What about this "optimalization" // file: y.h #include "x.h" class Y { //... X x_; }; // file: y.cpp Y::Y() { } ========= ver 2 ============= // file y.h class X; class Y { //... X *px_; }; // file: y.cpp Y::Y() : px_(new X) { } ~Y::Y() { delete px_; } ========= ver 3 =============== // file: y.h class Y { //... static const size_t sizeofx = /* ... */; 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(); } Questins: - What is the Pimpl space overhead - What is the performance overhead - What about the 3rd version Space overhead: #include <iostream> using namespace std; struct X { char c; struct XImpl *pimpl_; }; struct XImpl { char c; }; int main() { cout << sizeof(XImpl) << '\t' << sizeof(X) << endl; return 0; } // result: 1 8 Runtime overhead: - allocation/deallocation cost: relativelly expensive - indirect access of private members (+ back pointer) Critiques of 3rd "solution": 1. alignment problems: new guaranties, that object will align properly, char[] buffers doesn't! 2. X must not use the default assignment operator=() 3. If sizeof(XImpl) grows greater then sizeofx, we need to update the source. // file x.h class X { //... struct XImpl *pimpl_; }; // file x.cpp #include "x.h" struct XImpl { // private stuff here ... static void *operator new(size_t) { /*...*/ } static void *operator delete(void*) { /*...*/ } }; X::X() : pimpl_( new XImpl ) { } X::~X() { delete pimpl_; pimpl_ = 0; }