#ifndef HEADERFILENAME_H #define HEADERFILENAME_H /* contents of headerfilename.h here ... */ #endif
In C++, C library header file is always the C name prefixed
with the letter c in which the .h file suffix has been dropped,
for example <cassert> and <cstdlib>. To use the
C++ named C library need explicitly make it visible with
using directive using namespace std;
#ifndef __cplusplus #error Must use C++ for this application. #endif
The name __STDC__
is defined when compiling
Standard C. Of course, __cplusplus
and
__STDC__
are never defined at the same time.
#ifdef __cplusplus extern "C" { #endif /* all C declarations come here ... */ #ifdef __cplusplus } #endifThis linkage directive shows the function is written in a different programming language. Use
extern "C"
linkage directive to make C function available to C++
program and vice versa.
// single statement linkage directive extern "C" void exit(int); // compound statement linkage directive extern "C" { #include <cmath> // all are C functions int printf(const char* ...); int scanf(const char* ...); } extern "C" double calc(double dparm) // can be called from C programsNaturally, this works only for non-member functions. If want to call member functions (include virtual functions) from C, need to provide a simple wrapper. If want to call overloaded functions from C, must provide wrappers with distinct names for the C code to use.
// C++ code: class C { // ... virtual double f(char); }; extern "C" double call_C_f(C* p, int i) // wrapper function { return p->f(i); } void f(int); void f(double); extern "C" void f_i(int i) { f(i); } extern "C" void f_d(double d) { f(d); } /* C code: */ double call_C_f(struct C* p, int i); void f_i(int); void f_d(double); void ccc(struct C* p, int i) { double d = call_C_f(p,i); f_i(i); f_d(d); /* ... */ }
These techniques can be used to call a C++ library from C code even if cannot (or do not want to) modify the C++ headers.
extern "C"
is the only linkage specification
guaranteed to be supported by all C++ implementations. Others
likes extern "Ada"
and
extern "FORTRAN"
are implementation-specific.
#ifdef WIN32 // in Windows environment #include "Include\HeaderFileName.h" #else // in Unix/ Linux environment #include "Include/HeaderFileName.h" #endif
if ( element_count == 0 ) cerr << "Error: " << __FILE__ << " : line " << __LINE__ << "element_count must be non-zero.\n";
defined
identifier is useful when need to have
#if
, #elif
, #endif
as
there are only #ifdef
or #ifndef
available, no something like "#elifdef".
Some care should be taken in designing header files. The declarations provided should logically belong together. A header file takes time to compile. If it is too large or filled with too many disparate elements, programmers may be reluctant to incur the compile-time cost of including it.
Header file should never contain a definition for a non-inline function or an object. The inclusion of any of these definitions in two or more files of the same program will result in a linker error complaining about multiple definitions.// Error extern int ival = 10; // non-inline because explicit initialization double fica_rate; extern void dummy() {} const char* msg = "?? oops: error: "; // OK, inline typedef unsigned char uchar; const uchar INLINE = 128; inline bool is_relational( uchar tok ) { /* ... */ } const char *const msg = "?? oops: error: ";
Precompiled headers can sometimes help; but sometimes they can make the problem worse, too. Turning on precompiled headers may result in large dumps which take longer to read in than the headers they replace. The lesson to be learned is to keep headers as small as possible. When developing a library, provide many small headers which must be included separately, rather than one header which includes the whole library.
<iostream>
is known to take relatively
long to compile because they are made up of several other
internal headers and template code. To speed up, replace
it with the lightweight header <iosfwd>
in the header files, but still require
<iostream>
in the implementation files.
#include <string> #include "date.h" #include "address.h" #include "country.h" class Person { public: Person(const string& name, const Date& birthday, const Address& addr, const Country& country); string GetName() const { return name_; } protected: // implementation detail string name_; Date birthDate_; Address address_; Country citizenship_; };
Unfortunately, this sets up a compilation dependency between
the file defining Person
and these include files.
As a result, if any of these auxiliary classes changes its
implementation, or if any of the classes on which it depends
changes its implementation, the file containing the
Person
class must be recompiled, as must any
files that use the Person
class.
class string; class Date; class Address; class Country; class Person { public: Person(const string& name, const Date& birthday, const Address& addr, const Country& country); ~Person(); // need dynamic allocate/deallocate pimpl string GetName() const { return pimpl->GetName(); } protected: class PersonImpl; PersonImpl *pimpl; };
class string; class Date; class Address; class Country; class Person { public: // work like constructor and destructor static Person* MakePerson(const string& name, const Date& birthday, const Address& addr, const Country& country); static void DestroyPerson(Person*); virtual ~Person(); virtual string GetName() const = 0; }; // in implementation file class RealPerson; Person* MakePerson(const string& name, const Date& birthday, const Address& addr, const Country& country) { return new RealPerson(name, birthday, addr, country); } void DestroyPerson(Person* person) { delete person; // call ~RealPerson() and then ~Person() } // in another header file class RealPerson: public Person { public: RealPerson(const string& name, const Date& birthday, const Address& addr, const Country& country) : name_(name), birthday_(birthday), address_(addr), country_(country) { } virtual ~RealPerson() { } string GetName() const { return name_; } protected: string name_; Date birthday_; Address address_; Country country_; };
So use these techniques during development to minimize the impact on user when implementations change. Replace them with concrete classes for production use when it can be shown that the difference in speed and/ or size is significant enough to justify the increased coupling between classes.
There are three immediate implications to reduce compilation dependency:#include
header files unless it won't
compile without them. Instead, manually declare the classes needed
and let user#include
the additional headers necessary
to make their code compile. This is why <iosfwd>
is introduced.