Я всегда видел, как люди пишут
class.h
#ifndef CLASS_H
#define CLASS_H
//blah blah blah
#endif
Вопрос в том, почему они не делают этого для файла .cpp, который содержит определения для функций класса?
Допустим, у меня есть main.cpp
и main.cpp
включает class.h
. class.h
Файл не include
что - нибудь, так как же main.cpp
знает , что в class.cpp
?
FILE_H
, а неCLASS_H
.Ответы:
Во-первых, чтобы ответить на ваш первый запрос:
Когда вы видите это в .h файле:
Это препроцессорная техника предотвращения многократного включения заголовочного файла, что может быть проблематичным по разным причинам. Во время компиляции вашего проекта каждый файл .cpp (обычно) компилируется. Проще говоря, это означает, что компилятор возьмет ваш файл .cpp , откроет все его файлы
#included
, объединит их все в один массивный текстовый файл, а затем выполнит синтаксический анализ и, наконец, преобразует его в некоторый промежуточный код, оптимизирует / выполняет другие. задач и, наконец, сгенерировать выходные данные сборки для целевой архитектуры. Из-за этого, если файл#included
несколько раз под одним .cppфайл, компилятор добавит содержимое своего файла дважды, поэтому, если в этом файле есть определения, вы получите ошибку компилятора, сообщающую, что вы переопределили переменную. Когда файл обрабатывается на этапе препроцессора в процессе компиляции, при первом достижении его содержимого первые две строки проверят, был лиFILE_H
он определен для препроцессора. Если нет, он определитFILE_H
и продолжит обработку кода между ним и#endif
директивой. В следующий раз, когда препроцессор увидит содержимое этого файла, проверкаFILE_H
будет ложной, поэтому он будет сразу же сканировать до#endif
и продолжать после него. Это предотвращает ошибки переопределения.И для решения вашей второй проблемы:
В программировании на C ++ в качестве общей практики мы разделяем разработку на два типа файлов. Один с расширением .h, и мы называем это «заголовочный файл». Обычно они предоставляют объявление функций, классов, структур, глобальных переменных, typedefs, макросов и определений предварительной обработки и т. Д. По сути, они просто предоставляют вам информацию о вашем коде. Затем у нас есть расширение .cpp, которое мы называем «файл кода». Это обеспечит определения для этих функций, членов класса, любых элементов структуры, которым нужны определения, глобальные переменные и т. Д. Таким образом, файл .h объявляет код, а файл .cpp реализует это объявление. По этой причине мы обычно во время компиляции компилируем каждый .cppфайл в объект и затем связать эти объекты (потому что вы почти никогда не видите, чтобы один файл .cpp включал другой файл .cpp ).
Как эти внешние проблемы решаются - работа для компоновщика. Когда ваш компилятор обрабатывает main.cpp , он получает объявления для кода в class.cpp , включая class.h . Нужно только знать, как выглядят эти функции или переменные (что дает вам объявление). Поэтому он компилирует ваш файл main.cpp в некоторый объектный файл (назовите его main.obj ). Аналогично, class.cpp компилируется в class.objфайл. Чтобы создать конечный исполняемый файл, вызывается компоновщик, который связывает эти два объектных файла вместе. Для любых неразрешенных внешних переменных или функций компилятор поместит заглушку, где происходит доступ. Затем компоновщик возьмет эту заглушку и найдет код или переменную в другом указанном объектном файле, и, если он найден, он объединит код из двух объектных файлов в выходной файл и заменит заглушку с окончательным расположением функции или переменная. Таким образом, ваш код в main.cpp может вызывать функции и использовать переменные в class.cpp ЕСЛИ И ТОЛЬКО ЕСЛИ ОНИ ОБЪЯВЛЕНЫ в class.h .
Я надеюсь, что это было полезно.
источник
CLASS_H
Это включает охрану ; он используется для предотвращения многократного включения одного и того же заголовочного файла (по разным маршрутам) в один и тот же файл CPP (или, точнее, в одну и ту же единицу перевода ), что может привести к ошибкам с множественным определением.Включение защиты не требуется для файлов CPP, поскольку по определению содержимое файла CPP читается только один раз.
Похоже, вы интерпретировали включающие охранники как выполняющие ту же функцию, что и
import
операторы в других языках (таких как Java); однако это не так. Само по#include
себе примерно эквивалентноimport
другим языкам.источник
Это не так - по крайней мере, на этапе компиляции.
Перевод программы на С ++ из исходного кода в машинный код выполняется в три этапа:
class.h
вставляется вместо строки#include "class.h
. Поскольку вы можете быть включены в ваш заголовочный файл в нескольких местах,#ifndef
пункты избегают повторяющихся ошибок объявления, поскольку директива препроцессора не определена только при первом включении заголовочного файла.Таким образом, объявления могут быть переданы через заголовочный файл, в то время как сопоставление объявлений с определениями выполняется компоновщиком.
источник
Это различие между декларацией и определением. Заголовочные файлы обычно включают только объявление, а исходный файл содержит определение.
Чтобы использовать что-то, вам нужно знать, что это декларация, а не определение. Только компоновщик должен знать определение.
Вот почему вы включите заголовочный файл в один или несколько исходных файлов, но вы не включите исходный файл в другой.
Также ты имеешь ввиду
#include
и не импорт.источник
Это делается для заголовочных файлов, так что содержимое появляется только один раз в каждом предварительно обработанном исходном файле, даже если оно включено более одного раза (обычно потому, что оно включено из других заголовочных файлов). При первом включении символ
CLASS_H
(известный как « включить защиту» ) еще не был определен, поэтому все содержимое файла включено. Это определяет символ, поэтому, если он будет включен снова, содержимое файла (внутри блока#ifndef
/#endif
) будет пропущено.Нет необходимости делать это для самого исходного файла, поскольку (обычно) он не включен ни в какие другие файлы.
Ваш последний вопрос
class.h
должен содержать определение класса и объявления всех его членов, связанных функций и всего остального, чтобы в любом файле, в котором он содержится, было достаточно информации для использования класса. Реализации функций могут идти в отдельном исходном файле; вам нужны только декларации, чтобы позвонить им.источник
main.cpp не должен знать, что находится в class.cpp . Он просто должен знать объявления функций / классов, которые он использует, и эти объявления находятся в class.h .
Линкер связывает места, где используются функции / классы, объявленные в class.h , и их реализации в class.cpp.
источник
.cpp
файлы не включены (не используются#include
) в другие файлы. Поэтому им не нужно включать охрану.Main.cpp
будет знать имена и подписи класса, в котором вы реализовали,class.cpp
только потому, что вы все это указалиclass.h
- это цель файла заголовка. (Вы должны убедиться, чтоclass.h
точно описывает код, в котором вы реализуетеclass.cpp
.) Благодаря усилиям компоновщика исполняемый кодclass.cpp
будет доступен для исполняемого кодаmain.cpp
.источник
Обычно ожидается, что такие модули кода, как
.cpp
файлы, компилируются один раз и связаны с несколькими проектами, чтобы избежать ненужной повторной компиляции логики. Например,g++ -o class.cpp
будет производить,class.o
который вы могли бы затем связать из нескольких проектов для использованияg++ main.cpp class.o
.#include
Как вы, вероятно, подразумеваете, мы могли бы использовать его в качестве компоновщика, но было бы глупо, если бы мы знали, как правильно ссылаться, используя наш компилятор с меньшим количеством нажатий клавиш и менее расточительным повторением компиляции, а не с нашим кодом с большим количеством нажатий клавиш и более расточительным повторение компиляции ...Однако заголовочные файлы по-прежнему необходимо включать в каждый из нескольких проектов, поскольку это обеспечивает интерфейс для каждого модуля. Без этих заголовков компилятор не знал бы ни о каком символе, представленном
.o
файлами.Важно понимать, что заголовочные файлы - это то, что вводит определения символов для этих модулей; как только это будет реализовано, то имеет смысл, что множественные включения могут вызывать переопределения символов (что приводит к ошибкам), поэтому мы используем защитные ключи include для предотвращения таких переопределений.
источник
его из-за заголовочных файлов определяют, что класс содержит (члены, структуры данных) и файлы cpp реализуют его.
И, конечно же, основная причина этого заключается в том, что вы можете включить один файл .h несколько раз в другие файлы .h, но это приведет к нескольким определениям класса, что недопустимо.
источник