Cpp how to include files

Headers and Includes: Why and How

*************************************************
** 0) Introduction **
*************************************************

This article is meant to address a common newbie problem regarding failure to understand #include, headers, and source file interaction. Several good practices are outlined and explained to show how to avoid some ugly snags. Later sections get into more advanced topics (inlining and templates), so even C++ coders with some experience under their belt might benefit from a read!

If you are already familiar with the basics, feel free to skip ahead to section 4. That is where practices and design strategies are discussed.

*************************************************
** 1) Why we need header files. **
*************************************************

If you’re just starting out in C++, you might be wondering why you need to #include files and why you would want to have multiple .cpp files for a program. The reasons for this are simple:

(1) It speeds up compile time. As your program grows, so does your code, and if everything is in a single file, then everything must be fully recompiled every time you make any little change. This might not seem like a big deal for small programs (and it isn’t), but when you have a reasonable size project, compile times can take several minutes to compile the entire program. Can you imagine having to wait that long between every minor change?

Compile / wait 8 minutes / «oh crap, forgot a semicolon» / compile / wait 8 minutes / debug / compile / wait 8 minutes / etc

(2) It keeps your code more organized. If you seperate concepts into specific files, it’s easier to find the code you are looking for when you want to make modifications (or just look at it to remember how to use it and/or how it works).

(3) It allows you to separate interface from implementation. If you don’t understand what that means, don’t worry, we’ll see it in action throughout this article.

Those are the upsides, but the big, obvious downside is that is makes things a little more complicated if you don’t understand how it all works (in reality, though, it’s simpler than the alternatives as the project gets larger)

C++ programs are built in a two stage process. First, each source file is compiled on its own. The compiler generates intermediate files for each compiled source file. These intermediate files are often called object files — but they are not to be confused with objects in your code. Once all the files have been individually compiled, it then links all the object files together, which generates the final binary (the program).

This means that each source file is compiled separately from other source files. As a result of this, in terms of compiling, «a.cpp» is clueless as to what’s going on inside of «b.cpp». Here’s a quick example to illustrate:

// in myclass.cpp class MyClass < public: void foo(); int bar; >; void MyClass::foo() < // do stuff >
// in main.cpp int main() < MyClass a; // Compiler error: 'MyClass' is unidentified return 0; >

Even though MyClass is declared in your program, it is not declared in main.cpp, and therefore when you compile main.cpp you get that error.

Читайте также:  Upgrade pip python windows 10

This is where header files come in. Header files allow you to make the interface (in this case, the class MyClass) visible to other .cpp files, while keeping the implementation (in this case, MyClass’s member function bodies) in its own .cpp file. That same example again, but tweaked slightly:

// in myclass.h class MyClass < public: void foo(); int bar; >;
// in myclass.cpp void MyClass::foo()
//in main.cpp // defines MyClass int main() < MyClass a; // no longer produces an error, because MyClass is defined return 0; >

The #include statement is basically like a copy/paste operation. The compiler will «replace» the #include line with the actual contents of the file you’re including when it compiles the file.

***************************************************************
** 2) What is the difference between .h/.cpp/.hpp/.cc/etc **
***************************************************************

All files are fundamentally the same in that they’re all text files, however different kinds of files should have different extensions:

Header files should use a .h__ extension (.h / .hpp / .hxx). Which of those you use doesn’t matter.
C++ Source files should use a .c__ extention (.cpp / .cxx / .cc). Which of those you use doesn’t matter.
C Source files should use .c (.c only).

The reason C and C++ source files are seperated here is because it makes a difference in some compilers. In all likelyhood (since you’re reading this on a C++ site), you’ll be using C++ code, so refrain from using the .c extension. Also, files with header extensions might be ignored by the compiler if you try to compile them.

So what’s the difference between Header files and Source files? Basically, header files are #included and not compiled, whereas source files are compiled and not #included. You can try to side-step these conventions and make a file with a source extension behave like a header or vice-versa, but you shouldn’t. I won’t list the many reasons why you shouldn’t (other than the few I already have) — just don’t.

The one exception is that it is sometimes (although very rarely) useful to include a source file. This scenario has to do with instantiating templates and is outside the scope of this article. For now. just put it in your brain: «do not #include source files» *****************************************************
** 3) Include guards **
*****************************************************

C++ compilers do not have brains of their own, and so they will do exactly what you tell them to. If you tell them to include the same file more than once, then that is exactly what they will do. And if you don’t handle it properly, you’ll get some crazy errors as a result:

// myclass.h class MyClass < void DoSomething() < >>;
// main.cpp // define MyClass // Compiler error - MyClass already defined 

Now you might be saying to yourself «well that’s stupid, why would I include the same file twice?» Believe it or not, it will happen a lot. Not quite as illustrated above, though. Usually it happens when you include two files that each include the same file. Example:

Читайте также:  Справочник питона на русском

Источник

#include директива (C/C++)

Указывает препроцессору включить содержимое указанного файла в точку, где появляется директива.

Синтаксис

#include « path-spec «
#include path-spec >

Комментарии

Определения констант и макросов можно упорядочить во включаемые файлы (также называемые файлами заголовков), а затем использовать #include директивы для их добавления в любой исходный файл. Включаемые файлы также позволяют внедрять объявления внешних переменных и сложных типов данных. Типы можно определять и именовать только один раз во включаемом файле, созданном с этой целью.

Спецификация пути — это имя файла, которому при необходимости может предшествовать спецификация каталога. Имя файла должно указывать на существующий файл. Синтаксис path-spec зависит от операционной системы, в которой компилируется программа.

Сведения о том, как ссылаться на сборки в приложении C++, скомпилированном с помощью /clr , см. в разделе #using Директива .

Обе формы синтаксиса приводят к замене #include директивы всем содержимым указанного файла. Разница между двумя формами заключается в порядке путей, которые выполняет препроцессор, когда путь указан не полностью. В приведенной ниже таблице показывается различие между этими формами синтаксиса.

1) В том же каталоге, что и файл, содержащий инструкцию #include .

2) В каталоги открытых в настоящее время файлов включите в обратном порядке, в котором они были открыты. Поиск начинается в каталоге родительского включаемого файла, а затем выполняется в каталогах всех включаемых файлов-прародителей.

3) По пути, заданному каждым /I параметром компилятора.

1) По пути, указанному каждым /I параметром компилятора.

Как только препроцессор найдет файл с заданным именем, поиск останавливается. Если вы заключаете полную, однозначную спецификацию пути для включаемого файла в двойные кавычки ( » » ), препроцессор выполняет поиск только этой спецификации пути и игнорирует стандартные каталоги.

Если имя файла, заключенное в двойные кавычки, является неполным спецификацией пути, препроцессор сначала выполняет поиск в каталоге родительского файла. Родительский файл — это файл, содержащий директиву #include . Например, если включить файл с именем file2 в файл с именем file1, file1 будет родительским файлом.

Включаемые файлы могут быть вложенными: #include директива может отображаться в файле с именем другой #include директивы. Например, file2 может включать file3. В этом случае file1 по-прежнему будет родительским для file2, но это будет бабушка и дедушка file3.

Если вложенные файлы и компиляция выполняется в командной строке, поиск по каталогу начинается в каталоге родительского файла. Затем он проходит через каталоги любых файлов бабушек и дедушек. Таким образом, поиск начинается относительно каталога, в котором находится исходный файл, обрабатываемый в текущий момент. Если файл не найден, поиск перемещается в каталоги, указанные параметром компилятора /I (Дополнительные каталоги включения). Наконец, выполняется поиск каталогов, указанных в переменной INCLUDE среды.

В среде INCLUDE разработки Visual Studio переменная среды игнорируется. Вместо этого используются значения, указанные в свойствах проекта для каталогов include. Дополнительные сведения о настройке каталогов включения в Visual Studio см. в разделах Каталоги включения и Дополнительные каталоги включения.

В приведенном ниже примере демонстрируется включение файлов с помощью угловых скобок:

В примере содержимое файла с именем stdio.h добавляется в исходную программу. Угловые скобки приводят к тому, что препроцессор выполняет поиск каталогов, указанных INCLUDE переменной среды для stdio.h , после поиска каталогов, указанных параметром /I компилятора.

В следующем примере демонстрируется включение файлов, заданных в кавычках:

В примере содержимое файла, указанного параметром defs.h , добавляется в исходную программу. Кавычки означают, что препроцессор сначала попытается найти этот файл в каталоге, содержащем родительский исходный файл.

Для включаемых файлов поддерживается до 10 уровней вложения. Когда обработка вложенного #include объекта завершена, препроцессор продолжает вставлять вложенный родительский файл include в исходный файл.

Только для систем Майкрософт

Чтобы найти исходные файлы для включения, препроцессор сначала выполняет поиск в каталогах, указанных параметром /I компилятора. /I Если параметр отсутствует или если он завершается сбоем, препроцессор использует INCLUDE переменную среды для поиска включаемого файла в угловых скобках. Переменная INCLUDE среды и /I параметр компилятора могут содержать несколько путей, разделенных точкой с запятой ( ; ). Если в параметре или в INCLUDE переменной /I среды отображается несколько каталогов, препроцессор ищет их в том порядке, в котором они отображаются.

Представим себе следующую команду:

приводит к тому, что препроцессор выполняет поиск в каталоге D:\msvc\include\ для включения файлов, таких как stdio.h . Ниже еще один пример:

SET INCLUDE=D:\msvc\include CL myprog.c 

Эта инструкция действуют точно так же. Если найти файл в обоих наборах каталогов не удастся, возникает неустранимая ошибка компилятора.

Если имя файла полностью указано для включаемого файла с путем к двоеточию (например, F:\MSVC\SPECIAL\INCL\TEST.H ), препроцессор следует за путем.

Для включаемых файлов, указанных как #include «path-spec» , поиск в каталоге начинается в каталоге родительского файла, а затем переходит к каталогам всех файлов бабушки и дедушки. То есть поиск начинается относительно каталога, содержащего исходный файл, который обрабатывается. Если файл бабушки и дедушки не найден, поиск продолжится так, как если бы имя файла было заключено в угловые скобки.

КОНЕЦ Только для систем Майкрософт

Источник

Оцените статью