There are two kinds of include files in C++. those which contain code that is accepted by both ANSI-C and C++ compilers and those which contain code that is only accepted by C++ compilers. It is appropriate to distinguish between the two in order to avoid unpleasant compilation errors (from using the wrong kind of include file).
If a ".cc" file contains a large number of function definitions, the object file produced by the compiler may be unnecessarily large. In order to obtain the smallest possible executable files, it is necessary to have a separate file for each function definition. This is because the standard UNIX linker ld links all functions in an object file even if only one of them is actually used. It is especially important to remember that virtual functions are always linked. On the other hand, there are problems in managing a large number of files, since sufficiently powerful tools are not currently available. Also, the time necessary to compile a program consisting of a large number of files is longer.
Some debuggers cannot debug inline functions. By placing inline functions in a separate file and by including that file in the implementation file, thus treating the inline functions as ordinary functions, it is possible to debug the functions while testing the program. For this to work some special preprocessor techniques must be used. The inline definition file must not be included by the include file for the class and the keyword 'inline' must be removed.
When tools for managing C++ code are not available, it is much easier for those who use and maintain classes if there is only one class definition in each file and if implementations of member functions in different classes are not present in the same file.
Example 1 Inline definitions in a separate file for conditional compilation
// AnyClass.hh #ifndef OUTLINE #include "AnyClass.icc" #endif //AnyClass.cc #ifdef OUTLINE #define inline #include "AnyClass.icc" #undef inline #endif
| AT&T s Cfront-based compiler creates two functions
for every file in order to call constructors and destructors of static
objects in the proper order. These functions are named:
char__sti__file_cc__Fv(); //filename is file.cc char__std__file_cc__Fv(); //filename is file.cc It is easily understoodthat If a program has two files with the same name but in different subdirectories, there will be name collisions between the functions generated above, |
Note that comments in include files are meant for the users of classes, while comments in implementation files are meant for those who maintain the classes.
All our code must be copyright marked. If the code has been developed over a period of years, each year must be stated.
The standardization of comments makes it possible to automatically generate man-pages from source code. This may be used to keep source code and documentation together until adequate tools for information management are available.
Comments are often said to be either strategic or tactical. A strategic comment describes what a function or section of code is intended to do, and is placed before this code. A tactical comment
describes what a single line of code is intended to do, and is placed, if possible, at the end of this line. Unfortunately, too many tactical comments can make code unreadable. For this reason, it is
recommended to primarily use strategic comments, unless trying to explain very complicated code.
If the characters // are consistently used for writing comments, then the combination / * */ may be used to make comments out of entire sections of code during the development and debugging
phases. C++, however, does not allow comments to be nested using / * */.
Example 2 Documentation of a file
// // File: test.cc // Description: This is a test program // Rev. A // Created: Thur. Oct 31, 1991, 12:30:14 // Author: Erik Nyquist // mail: erik.nyquist@eua.ericsson.se // // Copyright Ellemtel Utvecklings AB 1991 // BOX 1505 // 125 25 ALVSJO // SWEDEN // tel int + 46 8 727 3000 // // The copyright to the computer program(s) herein // is the property of Ellemtel Utvecklings AB, Sweden. // The program(s) may be used and/or copied only with // the written permission of Ellemtel Utvecklings AB // or in accordance with the terms and conditions // stipulated in the agreement/contract under which // the program ( s) have been supplied. //Example 3 Strategic and Tactical Comments
// THE NEXT TWO LINES ARE STRATEGIC COMMENTS
// This function does some complicated things. It works like this:
// blah-blah-blah . . .
int
insanelyGreatAndComplicatedFunction ( int i )
{
  int index = i++ + ++i * i-- - --i // THIS IS A TACTICAL COMMENT
  return index;
}
The number of files included should be minimized. If a file is included in an include file, then every implementation file that includes the second include file must be re-compiled whenever the first file is modified. A simple modification in one include file can make it necessary to re-compile a large number of files.
When only referring to pointers or references to types defined in a file, it is often not necessary to include that file. It may suffice to use a forward declaration to inform the compiler that the class exists.Another alternative is to precede each declaration of a pointer to the class with the keyword class. True portable code is independent of the underlying operating system. For this reason, relative UNIX search paths should be avoided when including files. The processing of such search paths depends on the compiler and UNIX should not be taken for granted. Instead, search paths should be provided in 'make' files as options for the compiler.
If a file only contains information that is only needed in an implementation file, that file should not be included in another include file. Otherwise, when the information is no longer needed in the implementation file, it may be necessary to re-compile each file that uses the interface defined in the include file.
Every C++ course teaches the difference between the include directives for user-prepared and for library include files. If the file name is bracketed between "<" and ">", the preprocessor will not search for the file in the default directory. This reduces the risk of unintended name collisions between user-prepared and library include files.
By declaring a local constant string, the compiler becomes self-identifying. This may be used to easily determine the version of the program that is used. The string must begin with the characters @ (#) to be read by the UNIX what command.
Example 4 Technique for preventing multiple inclusion of an include file
#ifndef FOO_HH #define FOO_HH // The rest of the file #endifExample 5 Never use explicit UNIX path names
// NOT RECOMMENDED #include <../include/fnutt.h> // NOT GUARANTEED TO WORK #include <sys/socket.h>Example 6 Local constant string for identifying implementation files.
static const char* sccsid = "@(#) Exception.cc, rev. A, Copyright Ellemtel Utvecklings AB 1991"' ;Example 7 Include file for the class PackableString
// file: PackableString.hh
#ifndef PACKABLESTRING_HH
#define PACKABLESTRING_HH
#include "String.hh"
#include "Packable.hh"
// It is not necessary to extern-declare class Buffer when
// each pointer declaration specifies
// the keyword class as shown below.
// An explicit extern-declaration makes the code easier to
// understand.
extern class Buffer;
class packableString : public String, public Packable
{
  public:
    PackableString( const String& s );
    class Buffer* put( class Buffer* outbuffer );
    // . . .
} ;
#endif
Example 8 Implementation file for the class PackableString
// PackableString.cc
#include "PackableString.hh"
// To be able to use Buffer-instances, Buffer.hh MUST be included.
#include "Buffer.hh"
Buffer* PackableString. :put ( Buffer* outbuffer )
{
    // ...
}