Header files (C++)
The names of program elements such as variables, functions, classes, and so on must be declared before they can be used. For example, you can’t just write x = 42 without first declaring ‘x’.
int x; // declaration x = 42; // use x
The declaration tells the compiler whether the element is an int , a double , a function, a class or some other thing. Furthermore, each name must be declared (directly or indirectly) in every .cpp file in which it is used. When you compile a program, each .cpp file is compiled independently into a compilation unit. The compiler has no knowledge of what names are declared in other compilation units. That means that if you define a class or function or global variable, you must provide a declaration of that thing in each additional .cpp file that uses it. Each declaration of that thing must be exactly identical in all files. A slight inconsistency will cause errors, or unintended behavior, when the linker attempts to merge all the compilation units into a single program.
To minimize the potential for errors, C++ has adopted the convention of using header files to contain declarations. You make the declarations in a header file, then use the #include directive in every .cpp file or other header file that requires that declaration. The #include directive inserts a copy of the header file directly into the .cpp file prior to compilation.
In Visual Studio 2019, the C++20 modules feature is introduced as an improvement and eventual replacement for header files. For more information, see Overview of modules in C++.
Example
The following example shows a common way to declare a class and then use it in a different source file. We’ll start with the header file, my_class.h . It contains a class definition, but note that the definition is incomplete; the member function do_something is not defined:
Next, create an implementation file (typically with a .cpp or similar extension). We’ll call the file my_class.cpp and provide a definition for the member declaration. We add an #include directive for «my_class.h» file in order to have the my_class declaration inserted at this point in the .cpp file, and we include to pull in the declaration for std::cout . Note that quotes are used for header files in the same directory as the source file, and angle brackets are used for standard library headers. Also, many standard library headers do not have .h or any other file extension.
In the implementation file, we can optionally use a using statement to avoid having to qualify every mention of «my_class» or «cout» with «N::» or «std::». Don’t put using statements in your header files!
// my_class.cpp #include "my_class.h" // header in local directory #include // header in standard library using namespace N; using namespace std; void my_class::do_something()
Now we can use my_class in another .cpp file. We #include the header file so that the compiler pulls in the declaration. All the compiler needs to know is that my_class is a class that has a public member function called do_something() .
// my_program.cpp #include "my_class.h" using namespace N; int main()
After the compiler finishes compiling each .cpp file into .obj files, it passes the .obj files to the linker. When the linker merges the object files it finds exactly one definition for my_class; it is in the .obj file produced for my_class.cpp, and the build succeeds.
Include guards
Typically, header files have an include guard or a #pragma once directive to ensure that they are not inserted multiple times into a single .cpp file.
// my_class.h #ifndef MY_CLASS_H // include guard #define MY_CLASS_H namespace N < class my_class < public: void do_something(); >; > #endif /* MY_CLASS_H */
What to put in a header file
Because a header file might potentially be included by multiple files, it cannot contain definitions that might produce multiple definitions of the same name. The following are not allowed, or are considered very bad practice:
- built-in type definitions at namespace or global scope
- non-inline function definitions
- non-const variable definitions
- aggregate definitions
- unnamed namespaces
- using directives
Use of the using directive will not necessarily cause an error, but can potentially cause a problem because it brings the namespace into scope in every .cpp file that directly or indirectly includes that header.
Sample header file
The following example shows the various kinds of declarations and definitions that are allowed in a header file:
// sample.h #pragma once #include // #include directive #include namespace N // namespace declaration < inline namespace P < //. >enum class colors : short < red, blue, purple, azure >; const double PI = 3.14; // const and constexpr definitions constexpr int MeaningOfLife< 42 >; constexpr int get_meaning() < static_assert(MeaningOfLife == 42, "unexpected!"); // static_assert return MeaningOfLife; >using vstr = std::vector; // type alias extern double d; // extern variable #define LOG // macro definition #ifdef LOG // conditional compilation directive void print_to_log(); #endif class my_class // regular class definition, < // but no non-inline function definitions friend class other_class; public: void do_something(); // definition in my_class.cpp inline void put_value(int i) < vals.push_back(i); >// inline OK private: vstr vals; int i; >; struct RGB < short r< 0 >; // member initialization short g< 0 >; short b< 0 >; >; template // template definition class value_store < public: value_store() = default; void write_value(T val) < //. function definition OK in template >private: std::vector vals; >; template // template declaration class value_widget; >
Feedback
Submit and view feedback for
Class declaration
Classes are user-defined types, defined by class-specifier, which appears in decl-specifier-seq of the declaration syntax.
Contents
[edit] Syntax
The class specifier has the following syntax:
class-key attr (optional) class-head-name final (optional) base-clause (optional) member-specification > | (1) |
class-key attr (optional) base-clause (optional) member-specification > | (2) |
class-key | — | one of class, struct and union. The keywords class and struct are identical except for the default member access and the default base class access. If it is union , the declaration introduces a union type. |
attr | — | (since C++11) any number of attributes, may include alignas specifier |
class-head-name | — | the name of the class that’s being defined, optionally qualified |
final | — | (since C++11) if present, the class cannot be derived |
base-clause | — | list of one or more base classes and the model of inheritance used for each (see derived class) |
member-specification | — | list of access specifiers, member object and member function declarations and definitions (see below). |
[edit] Forward declaration
A declaration of the following form
class-key attr identifier ; |
Declares a class type which will be defined later in this scope. Until the definition appears, this class name has incomplete type. This allows classes that refer to each other:
class Vector; // forward declaration class Matrix { // . friend Vector operator*(const Matrix&, const Vector&); }; class Vector { // . friend Vector operator*(const Matrix&, const Vector&); };
and if a particular source file only uses pointers and references to the class, this makes it possible to reduce #include dependencies:
// in MyStruct.h #include // contains forward declaration of std::ostream struct MyStruct { int value; friend std::ostream& operator(std::ostream& os, const S& s); // definition provided in MyStruct.cpp file which uses #include };
If forward declaration appears in local scope, it hides previously declared class, variable, function, and all other declarations of the same name that may appear in enclosing scopes:
struct s { int a; }; struct s; // does nothing (s already defined in this scope) void g() { struct s; // forward declaration of a new, local struct "s" // this hides global struct s until the end of this block s* p; // pointer to local struct s struct s { char* p; }; // definitions of the local struct s }
Note that a new class name may also be introduced by an elaborated type specifier which appears as part of another declaration, but only if name lookup can’t find a previously declared class with the same name.
class U; namespace ns { class Y f(class T p); // declares function ns::f and declares ns::T and ns::Y class U f(); // U refers to ::U // can use pointers and references to T and Y Y* p; T* q; }
[edit] Member specification
The member specification, or the body of a class definition, is a brace-enclosed sequence of any number of the following:
1) Member declarations of the form
attr (optional) decl-specifier-seq (optional) member-declarator-list (optional) ; |
attr | — | (since C++11) any number of attributes |
decl-specifier-seq | — | sequence of specifiers. It is only optional in the declarations of constructors, destructors, and user-defined type conversion functions |
member-declarator-list | — | similar to an init-declarator-list, but additionally allows bit-field declaration, pure-specifier , and virt-specifier ( override or final ) (since C++11) , and does not allow direct-non-list-initialization syntax. |
This declaration may declare static and non-static data members and member functions, member typedefs, member enumerations, and nested classes. It may also be a friend declaration.
class S { int d1; // non-static data member int a[10] = {1,2}; // non-static data member with initializer (C++11) static const int d2 = 1; // static data member with initializer virtual void f1(int) = 0; // pure virtual member function std::string d3, *d4, f2(int); // two data members and a member function enum {NORTH, SOUTH, EAST, WEST}; struct NestedS { std::string s; } d5, *d6; typedef NestedS value_type, *pointer_type; };
2) Function definitions, which both declare and define member functions or friend functions. A semicolon after a member function definition is optional. All functions that are defined inside a class body are automatically inline , unless they are attached to a named module (since C++20) .
class M { std::size_t C; std::vectorint> data; public: M(std::size_t R, std::size_t C) : C(C), data(R*C) {} // constructor definition int operator()(std::size_t r, std::size_t c) const // member function definition { return data[r * C + c]; } int& operator()(std::size_t r, std::size_t c) // another member function definition { return data[r * C + c]; } };
class S { public: S(); // public constructor S(const S&); // public copy constructor virtual ~S(); // public virtual destructor private: int* ptr; // private data member };
class Base { protected: int d; }; class Derived : public Base { public: using Base::d; // make Base's protected member d a public member of Derived using Base::Base; // inherit all bases' constructors (C++11) };
templatetypename T> struct Foo { static_assert(std::is_floating_pointT>::value, "Foo: T must be floating point"); };