Share via


Soubory hlaviček (C++)

Názvy prvků programu, jako jsou proměnné, funkce, třídy atd., musí být deklarovány před jejich použitím. Například nemůžete psát jen x = 42 bez toho, abyste napřed deklarují "x".

int x; // declaration
x = 42; // use x

Deklarace říká kompilátoru, zda je intprvek , a double, funkce, class nebo něco jiného. Každý název musí být navíc deklarován (přímo nebo nepřímo) v každém souboru .cpp, ve kterém se používá. Při kompilaci programu se každý soubor .cpp kompiluje nezávisle na kompilační jednotce. Kompilátor nemá žádné znalosti o tom, jaké názvy jsou deklarovány v jiných kompilačních jednotkách. To znamená, že pokud definujete třídu nebo funkci nebo globální proměnnou, musíte zadat deklaraci této věci v každém dalším souboru .cpp, který ji používá. Každá deklarace této věci musí být ve všech souborech přesně stejná. Mírná nekonzistence způsobí chyby nebo nezamýšlené chování, když se linker pokusí sloučit všechny jednotky kompilace do jednoho programu.

Aby se minimalizoval potenciál chyb, jazyk C++ přijal konvenci použití souborů hlaviček k zahrnutí deklarací. Deklarace provedete v souboru hlaviček a pak použijete direktivu #include v každém souboru .cpp nebo jiném souboru hlaviček, který tuto deklaraci vyžaduje. Direktiva #include vloží kopii hlavičkového souboru přímo do souboru .cpp před kompilací.

Poznámka

V sadě Visual Studio 2019 se funkce modulů C++20 zavádí jako vylepšení a případná náhrada souborů hlaviček. Další informace naleznete v tématu Přehled modulů v jazyce C++.

Příklad

Následující příklad ukazuje běžný způsob deklarace třídy a jeho následné použití v jiném zdrojovém souboru. Začneme se souborem hlaviček. my_class.h Obsahuje definici třídy, ale všimněte si, že definice není úplná; členová funkce do_something není definována:

// my_class.h
namespace N
{
    class my_class
    {
    public:
        void do_something();
    };

}

Dále vytvořte implementační soubor (obvykle s příponou .cpp nebo podobnou příponou). Soubor zavoláme my_class.cpp a poskytneme definici deklarace člena. Přidáme direktivu #include pro soubor "my_class.h", abychom měli my_class deklaraci vloženou v tomto okamžiku do souboru .cpp a zahrneme <iostream> do deklarace pro std::cout. Všimněte si, že uvozovky se používají pro soubory hlaviček ve stejném adresáři jako zdrojový soubor a úhlové závorky se používají pro standardní hlavičky knihovny. Mnoho standardních hlaviček knihovny také nemá příponu .h ani žádnou jinou příponu souboru.

V souboru implementace můžeme volitelně použít using příkaz, abychom nemuseli opravovat každou zmínku "my_class" nebo "cout" s "N::" nebo "std::". Neumisťujte using příkazy do souborů hlaviček!

// my_class.cpp
#include "my_class.h" // header in local directory
#include <iostream> // header in standard library

using namespace N;
using namespace std;

void my_class::do_something()
{
    cout << "Doing something!" << endl;
}

Teď můžeme použít my_class v jiném souboru .cpp. Soubor hlaviček #include tak, aby kompilátor načítá deklaraci. Všichni kompilátor potřebuje vědět je, že my_class je třída, která má veřejnou členovou funkci volanou do_something().

// my_program.cpp
#include "my_class.h"

using namespace N;

int main()
{
    my_class mc;
    mc.do_something();
    return 0;
}

Jakmile kompilátor dokončí kompilaci každého souboru .cpp do souborů .obj, předá do linkeru soubory .obj. Když linker sloučí soubory objektů, najde přesně jednu definici pro my_class; je v souboru .obj vytvořeném pro my_class.cpp a sestavení bude úspěšné.

Zahrnout strážce

Soubory hlaviček mají obvykle ochranu include nebo direktivu #pragma once , aby se zajistilo, že se do jednoho souboru .cpp nevloží vícekrát.

// 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 */

Co vložit do souboru hlaviček

Vzhledem k tomu, že soubor hlaviček může být potenciálně zahrnut do více souborů, nemůže obsahovat definice, které by mohly vytvořit více definic stejného názvu. Následující možnosti nejsou povoleny nebo jsou považovány za velmi špatnou praxi:

  • předdefinované definice typů v oboru názvů nebo globálním oboru
  • Definice neline funkcí
  • definice proměnných, které nejsou const
  • agregační definice
  • nepojmenované obory názvů
  • using – direktivy

using Použití direktivy nemusí nutně způsobit chybu, ale může potenciálně způsobit problém, protože přenese obor názvů do oboru v každém souboru .cpp, který přímo nebo nepřímo obsahuje tuto hlavičku.

Ukázkový soubor záhlaví

Následující příklad ukazuje různé druhy deklarací a definic, které jsou povoleny v souboru hlavičky:

// sample.h
#pragma once
#include <vector> // #include directive
#include <string>

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<int>;  // 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 <typename T>  // template definition
    class value_store
    {
    public:
        value_store<T>() = default;
        void write_value(T val)
        {
            //... function definition OK in template
        }
    private:
        std::vector<T> vals;
    };

    template <typename T>  // template declaration
    class value_widget;
}