Author: Herb Sutter Translator: plpliuly
/ * This article is a 77 of the GotW (GURU of the Week) series of self-entertainment translations, the original copyright is the author of Herb Sutter (the famous C expert, "Exceptional C " author). The translation of this article did not have the consent of the original author, only for learning discussion. - Translator * /
# 7 Compile-Time Dependencies Difficulty: 7/10 Many people use #include with #include when writing C programs, contains some unnecessary headers. Are you also like this? See what we have to discuss below.
problem:
[Reminder: This problem will be difficult than it looks. Also please pay attention to the comments in the program. ] Contains too much unnecessary header files will seriously grow program build time, especially in a header file containing many source files, That too much header file.
In the header file below, which #include statement can be removed immediately? Second, which #include statement can be removed after proper adjustment? (You can't change the public interface of Class X and Class Y; that is, any changes you do cannot affect customer code).
// gotw007.h (importation file is gotw007.cpp) // #include "ah" // class a #include "bh" // class b #include "ch" // class c #include "dh" // class D // (NOTE: ONLY A and C Have Virtual Functions) #include #include
Class x: public a {public: x (const c &); d function1 (int, char *); d function1 (int, c); b & function2 (b); void function3 (std :: wostringstream &); std :: ostream & Print: std :: string name_; std :: list
Class Y: Private B {Public: c Function4 (a); private: std :: list
answer
First, see which headers don't have to include it completely. 1. We can immediately remove: -ioStream, because despite the use of streams, there is no special thing in any iosteam. -OSTEAM and SSTREAM, because parameters and return values are only available forward-declared). So just include iOSFWD. (Note There is no corresponding "stringfwd" or "listfwd" standard library header file; iOSFWD is the code that is compatible with the existing use of non-template STREAM subsystems) We cannot immediately remove the head The files are: -ah: Because A is the base class of X - BH: B is y's base class -Ch: Because many existing compiler requirements know the definition of C when instantiation List
Finally, see if you can continue to adjust to remove more header files: 3. Note that B is the private base class of Y and B has no virtual functions, we can remove B.H. The main reason for typically selecting private inheritance without selecting class aggregation or inclusive (nested classes) is to redefine the virtual function of the base class. Therefore, this should be changed to Yes having a B type member variable instead of inheriting from the Class B. In order to remove B.H, this member variable should be a hidden PIMPL_ part of Y. [Recommendation] Try to use PIMPL_ (which is pointing to implementation) will be separated from the customer code. The following text is extracted from the code specification of the GotW: - Package and Isolation - Avoid direct definition private members directly in the category state - use a non-transparent pointer to "struct XXXXIMPL * PIMPL_" to store private members (including private Member variables and members functions), such as Class Map {private: struct mapimpl * pimpl _;} (lakos96: 398-405; meyer92: 111-116; murray93: 72-74) 4. Currently we can't do any action against AH Because A is used to do X's public base class, and A has a virtual member function, so it is necessary to know or use this IS-A relationship in the customer code. However, notice that X and Y are simply not correlated, we can separate the definitions of X and Y to two different header files (assuming this header file is changed to a stub that includes XH and YH, so there is no need to change Existing code). In this case, at least y.h does not need to include A.h because it is only used as a function parameter, which is not required. Comprehensively synthesize the above content, we can get the following more information of the header file: file: // ---------------------------- ----------------------------------- // New file xh: only two incrudes! // #include " AH "// Class a #include
Class C; Class D; Class B; // Translator
Class x: public a {public: x (const c &); d function1 (int, char *); d function1 (int, c); b & function2 (b); void function3 (std :: wostringstream &); std :: ostream & Print (std :: ostream &) const; private: class ximpl * pimpl_;};
Inline std :: ostream & operator << (std :: ostream & os, const x) {return x.print (OS);} // NOTE: THIS DOES NOT Require Ostream's Definition!
File: // ---------------------------------------------- ----------------- // New file yh: zero incrudes! // Class A; Class C;
Class y {public: c function4 (a); private: class yimpl * pimpl_;}; file: // -------------------------- ------------------------------------- // Gotw007.h is now Just a Compatibility Stub with Two Lines, and // pulls inly two extra secondary inclus (through xh) // #include "xh" #include "yh"
File: // ---------------------------------------------- ----------------- // new structures in gotw007.cpp ... Note That the impl objects // Will Be.com and delete'd By the x / y dtors // and x / y member functions Will Access The Data THROUGH THEIR / / PIMPL_ POINTERS // Struct XIMPL // Yes, this can be called "even {// thing the forward-decl sign" Class "std :: string name_; std :: list
Struct Yimpl {std :: list
As you can see from the above header files, the customer code (source file) using X simply includes AH and iostwd; if you do not change the customer code, the customer code that uses Y is only needed to include AH and IOSFWD, if in the future Using Y's Customer Code Update will contain GOTW007.H to contain YH, the Y's customer code does not need to include any other header files for use Y. Compare the original header file, this is a big improvement!