在讀《Effective C++》和項目源代碼時,看到pImpl Idiom。它可以用來
降低文件間的編譯依賴關系,通過把一個Class分成兩個Class,一個只提供接口,另一個負責實現該接口,實現接口與實現的分離。這個分離的關鍵在于“以聲明的依賴性”替換“定義的依賴性”,而編譯依賴性最小化的本質是:讓頭文件盡可能的自我滿足,萬一做不到,則讓它與其他文件內的聲明式(而非定義式)相依。
引用
這里的一些描述:
The Pimpl idiom, also known as the compilation firewall or Cheshire Cat
technique, is a "private implementation" technique useful only in CeePlusPlus and statically compiled languages like it...
Benefits:
- Changing private member variables of a class does not require recompiling classes that depend on it, thus make times are faster, and the FragileBinaryInterfaceProblem is reduced.
- The header file does not need to #include classes that are used 'by value' in private member variables, thus compile times are faster.
- This is sorta like the way SmallTalk automatically handles classes... more pure encapsulation.
Drawbacks:
- More work for the implementor.
- Doesn't work for 'protected' members where access by subclasses is required.
- Somewhat harder to read code, since some information is no longer in the header file.
- Run-time performance is slightly compromised due to the
pointer indirection, especially if function calls are virtual (branch
prediction for indirect branches is generally poor).
How to do it:
- Put all the private member variables into a struct.
- Put the struct definition in the .cpp file.
- In the header file, put only the ForwardDeclaration of the struct.
- In the class definition, declare a (smart) pointer to the struct as the only private member variable.
- The constructors for the class need to create the struct.
- The destructor of the class needs to destroy the struct (possibly implicitly due to use of a smart pointer).
- The assignment operator and CopyConstructor need to copy the struct appropriately or else be disabled.
Code:
1???struct?AImp;
2???class?A?{
3???public:
4?????//?Same?public?interface?as?A,?but?all?delegated?to?concrete?implementation.
5???private:
6?????AImp?*?pimpl;
7???};
8?
If you use a SmartPointer
and you only have one implementation, there is no need to make any of
the member functions virtual, except possibly the destructor. The
run-time cost of non-virtual member function calls is much lower, and a
compiler that does whole-program optimization can inline them even
though they're in a separate translation unit. Here's an example:
?1??//?foo.h
?2?
?3???class?foo_impl;
?4?
?5???class?foo?{
?6?????//?Boilerplate
?7?????friend?class?foo_impl;
?8?????foo()?{}?//?so?only?foo_impl?can?derive?from?foo
?9?????const?foo_impl?*?impl()?const;
10?????foo_impl?*?impl();
11???public:
12?????virtual?~foo()?{}
13?????//?Factories
14?????static?std::auto_ptr<foo>?create(int?value);
15?????//?Interface
16?????int?value()?const;
17???};
18?
19???//?foo.cpp
20?
21???class?foo_impl?:?public?foo?{
22?????friend?class?foo;
23?????//?Constructors?mirroring?the?factory?functions?in?foo
24?????explicit?foo_impl(int?value)?:?value_(value)?{}
25?????//?Member?data
26?????int?value_;
27???};
28?
29???inline?const?foo_impl?*?foo::impl()?const?{
30?????return?static_cast<const?foo_impl?*>(this);
31???}
32???inline?foo_impl?*?foo::impl()?{
33?????return?static_cast<foo_impl?*>(this);
34???}
35?
36???std::auto_ptr<foo>?foo::create(int?value)?{
37?????return?std::auto_ptr<foo>(new?foo_impl(value));
38???}
39?
40???int?foo::value()?const?{?return?impl()->value_;?}
41?
42?
Here, the destructor needs to be declared virtual foo so that
std::auto_ptr<foo> calls foo_impl's destructor. If you use
boost::shared_ptr<foo> instead, even that doesn't need to be
virtual, because shared_ptr remembers how to call the correct
destructor. (This doesn't improve performance or memory use, because
shared_ptr is larger and slower than auto_ptr, but if you need to use
shared_ptr anyway you may as well eliminate the virtual destructor.) -- BenHutchings參考閱讀:
Effective C++
http://c2.com/cgi/wiki?PimplIdiom
http://en.wikipedia.org/wiki/Opaque_pointer