Как уже упоминалось в комментарии С.М. похоже, вы неправильно понимаете, для чего нужны заголовочные файлы и как они используются.
Чтобы правильно понять это, нужно знать три понятия:
В C ++ все символы должны быть оба объявлены и определены . Разница в том, что объявление сообщает компилятору, что символ существует где-то , тогда как определение является фактической реализацией или (действительно) определением символа.
Компилятор C ++ на самом деле имеет дело не с исходными файлами, а с единицами перевода . Короче говоря, единица перевода - это один исходный файл со всеми включенными заголовочными файлами.
Процесс сборки, который выполняется в несколько этапов:
- Редактирование исходных и заголовочных файлов
- Сборка исходных файлов (единиц перевода) в объектные файлы (каждый объектный файл представляет одну единицу перевода)
- Ссылка объектных файлов в конечный исполняемый файл
Большая часть работы выполняется единой программой внешнего интерфейса компилятора, которая может скрыть многие сложности за всем этим.
Теперь вернемся к заголовочным файлам и их использованию ...
Заголовочные файлы обычно используются для объявлений функций и переменных и определения пространств имен, структур и классов.
Файлы исходных файлов содержат определение (реализацию) функций, переменных и функций-членов структуры / класса.
Вы включаете необходимые файлы заголовков в исходные файлы. Исходные файлы не должны включать другие исходные файлы. Заголовочные файлы могут включать другие заголовочные файлы, но не должны включать (или импортировать) какие-либо исходные файлы.
Затем вы создаете исходные файлы отдельно и связываете их в единую и конечную исполняемую программу.
Может потребоваться простой пример.
Заголовочный файл foo.h
:
// Header include guard (to prevent multiple inclusion in a single translation unit)
#ifndef FOO_H
#define FOO_H
// Define the class Foo
class Foo
{
public:
// Declare the function hello
void hello();
};
// End of the header include guard
#endif
Исходный файл foo.cpp
:
// Include the header files we need
#include <iostream>
#include "foo.h"
// Define (implement) the member function
void Foo::hello()
{
std::cout << "Hello from Foo\n";
}
Заголовочный файл bar.h
:
// Header include guard (to prevent multiple inclusion in a single translation unit)
#ifndef BAR_H
#define BAR_H
// We use the Foo class, so need to include the header file where that class is defined
#include "foo.h"
// Define the class Bar
class Bar
{
public:
// The Bar default constructor, we define it inline
Bar()
: my_foo() // Constructor initializer list, constructs and initializes the member variables
{
// Empty body
}
// Declare the function my_hello
void my_hello();
private:
Foo my_foo;
};
// End of the header include guard
#endif
Исходный файл bar.cpp
:
#include "bar.h"
// Define the function
void Bar::my_hello()
{
// Call function from other class
my_foo.hello();
}
И чтобы связать все это вместе, файл main.cpp
:
// We will use the Bar class, so include the header file where it's defined
#include "bar.h"
int main()
{
Bar bar;
bar.my_hello();
}
Для построения этого (при условии, что macOS и компилятор clang++
) вы можете использовать следующие команды в терминале (при условии, что исходный и заголовочный файлы находятся в текущем каталоге):
$ clang++ -Wall foo.cpp -c
$ clang++ -Wall bar.cpp -c
$ clang++ -Wall main.cpp -c
$ clang++ foo.o bar.o main.o -o my_example_program
Для опций компилятора:
-Wall
включить больше предупреждений, что хорошо
-c
говорит программе внешнего интерфейса компилятора сгенерировать объектный файл из модуля перевода (вместо попытки создания исполняемого файла)
-o
для имени выходного файла
Если вы запустите программу
$ ./my_example_program
тогда должно получиться
Hello from Foo