共用方式為


標頭檔 (C++)

變數、函式、類別等程式專案的名稱必須先宣告,才能使用。 例如,您不只需要先宣告 'x' 即可撰寫 x = 42

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

宣告會告知編譯程序專案是否為 int、、函double式或class某些其他專案。 此外,每個名稱都必須在所使用的每個.cpp檔案中宣告(直接或間接)。 當您編譯程式時,每個.cpp檔案都會獨立編譯成編譯單位。 編譯程式不知道在其他編譯單位中宣告哪些名稱。 這表示,如果您定義類別或函式或全域變數,則必須在使用該類別的每個額外.cpp檔案中提供該專案的宣告。 所有檔案中該事物的每個宣告都必須完全相同。 當鏈接器嘗試將所有編譯單位合併成單一程式時,稍微不一致會導致錯誤或非預期的行為。

為了將錯誤的可能性降到最低,C++已採用使用 頭檔 來包含宣告的慣例。 您可以在頭檔中宣告,然後在每個.cpp檔案或其他需要該宣告的頭檔中,使用 #include 指示詞。 #include 指示詞會在編譯之前,直接將頭文件複本插入.cpp檔案中。

注意

在 Visual Studio 2019 中,會將 C++20 模組 功能引進為頭檔改善和最終取代。 如需詳細資訊,請參閱 C++ 中的模組概觀

範例

下列範例示範宣告類別的常見方式,然後在不同的原始程序檔中使用。 我們將從頭檔案開始, my_class.h。 它包含類別定義,但請注意定義不完整;未定義成員函 do_something 式:

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

}

接下來,建立實作檔案(通常具有.cpp或類似的擴展名)。 我們將呼叫檔案my_class.cpp並提供成員宣告的定義。 我們會新增 #include 「my_class.h」 檔案的指示詞,以便將my_class宣告插入.cpp檔案中,而且我們會在 的宣告<iostream>中加入 std::cout 。 請注意,引號用於與原始程式檔相同目錄中的頭檔,而角括弧則用於標準連結庫標頭。 此外,許多標準連結庫標頭沒有 .h 或任何其他擴展名。

在實作檔案中,我們可以選擇性地使用 using 語句,以避免使用 “n::” 或 “std::” 來限定每個提及 “my_class” 或 “cout”。 請勿將 語句放在 using 頭檔中!

// 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;
}

現在,我們可以在另一個.cpp檔案中使用 my_class 。 我們會 #include 頭檔,讓編譯程式提取宣告。 編譯程式必須知道的是,my_class是具有名為 do_something()之公用成員函式的類別。

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

using namespace N;

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

編譯程式完成將每個.cpp檔案編譯成.obj檔案之後,會將.obj檔案傳遞至連結器。 當連結器合併物件檔案時,它會找到my_class的一個定義;它位於針對my_class.cpp產生的.obj檔案中,建置會成功。

包含防護

頭檔通常會有 include guard#pragma once 指示詞,以確保它們不會多次插入單一.cpp檔案中。

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

要放入頭文件的內容

因為頭檔可能由多個檔案包含,所以不能包含可能會產生相同名稱多個定義的定義。 不允許下列專案,或被視為非常糟糕的做法:

  • 命名空間或全域範圍的內建類型定義
  • 非內嵌函式定義
  • 非 const 變數定義
  • 匯總定義
  • 未命名的命名空間
  • using 指示詞

using使用指示詞不一定會造成錯誤,但可能會造成問題,因為它會將命名空間帶入直接或間接包含該標頭的每個.cpp檔案範圍內。

範例頭檔

下列範例顯示頭檔中允許的各種宣告和定義:

// 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;
}