Как правильно использовать директиву #include?

Есть ли материал о том, как правильно пользоваться #include? Я не нашел ни одного учебника по C / C++, который бы подробно объяснял это использование. В формальном проекте я всегда запутываюсь в этом.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
16
0
20 979
10
Перейти к ответу Данный вопрос помечен как решенный

Ответы 10

Вот что меня всегда сбивало с толку:

Это ищет в пути заголовка:

#include <stdio.h>

Это ищет в вашем локальном каталоге:

#include "myfile.h"

Второе, что вы должны сделать с КАЖДЫМ заголовком:

myfilename.h:

#ifndef MYFILENAME_H
#define MYFILENAME_H
//put code here
#endif

Этот шаблон означает, что вы не можете отказаться от переопределения заголовков в вашей компиляции (Приветствия orsogufo за то, что он указал мне, что это называется «защитой включения»). Прочтите немного о том, как компилятор C на самом деле компилирует файлы (перед компоновкой), потому что это сделает мир #define и #include очень понятным для вас, компилятор C, когда дело доходит до синтаксического анализа текста, не очень разумный. (Сам компилятор C - другое дело)

Шаблон называется «включить охранник».

Paolo Tedesco 21.01.2009 12:35

И большинство компиляторов допускают эквивалент #pragma once

xtofl 21.01.2009 13:34

@xtofi: Нет. Это расширение MS. Очень немногие другие компиляторы поддерживают его.

Martin York 21.01.2009 15:38

@Spence: Нет, это неправильно. Второй include также ищет набор каталогов (просто другой набор)

Martin York 21.01.2009 15:41

@Martin: Действительно - только MS и gcc поддерживают #pragma один раз (а последний официально не поддерживает). Особенно сложно дать четкое определение понятию «один и тот же файл». Почему у нас нет эквивалента php "include" vs "require"?

xtofl 21.01.2009 15:56

@xtofl: Согласен, было бы неплохо. Но даже php не работает по той же причине: us2.php.net/require

Martin York 21.01.2009 16:16

_H и прописные буквы тоже важны. Однажды, когда у меня было меньше опыта, я использовал защиту включения с тем же именем и регистром, что и определение класса в файле ... какое-то время меня раздражало то, что он не компилируется.

Dan 21.01.2009 20:54

Мартин прав. разница между "" и <> не определена. его реализация определяет, в чем разница. большинство компиляторов, однако, сначала обращаются к пути, относящемуся к файлу, когда используется "".

Johannes Schaub - litb 22.01.2009 01:20
Ответ принят как подходящий
  • Если у вас есть деньги, посмотрите проект «Крупномасштабный программный продукт на C++» от Джона Лакоса.
  • В руководствах по кодированию Google C++ тоже есть кое-что.
  • Также проверьте материалы Sutter Herb в Интернете (блог).

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

Я думаю, что то, о чем вы говорили, - это то, что мне нужно. Все вышеперечисленное достаточно простое, чтобы все это знали.

MainID 21.01.2009 13:06

Заголовочные файлы - это способ C разделить интерфейс и реализацию. Они делятся на два типа: стандартные и определяемые пользователем файлы заголовков. Стандартный файл заголовка, такой как string.h, позволяет нам получить доступ к функциям базовой библиотеки C. Вы должны # включать его в каждый файл .c, который использует соответствующие функции. Обычно здесь используются скобки, как в #include Определяемый пользователем файл заголовка предоставляет вашу реализацию функций другим программистам или другим частям вашего кода C. Если вы реализовали модуль под названием рациональный.c для вычислений с рациональными числами, он должен иметь соответствующий файл рациональный.h для его общедоступного интерфейса. Каждый файл, который использует эту функциональность, должен # включать рациональный.h, а также рациональный.c должен # включать его. Обычно это делается с помощью #include "rational.h ". Часть компиляции, которая выполняет #includes, называется Препроцессор C. В основном это замена текста и вставка текста. Спенс прав в своем шаблоне предотвращения дублирования #includes, которые портят пространство имен. Это основа включения, GNU Make дает вам гораздо больше мощности, а также гораздо больше проблем.

Вы используете #include "yourfile.h", если yourfile.h находится в текущем рабочем каталоге. и #include <yourfile.h>, если путь к файлу yourfile.h был включен в каталоги включения C++ (где-то в конфигурации, например: c:\mylib\yourfile.h, путь c:\mylib\ должен быть указан как каталог включения) Также вы можете включить .cpp и .hpp (h плюс плюс). Существует определенный набор файлов, которые можно записать как: #include <iostream>. Для работы этого конкретного примера необходимо указать using namespace std;.

Есть очень хорошее программное обеспечение, которое интегрировано с Visual C++ от Microsoft и показывает пути включения. http://www.profactor.co.uk/includemanager.php

Для этого в конкретном примере работы вам необходимо указать using namespace std; <- здесь нет особого смысла, лучше уберите этот совет.
Wolf 13.12.2017 11:46

В дополнение к другим комментариям помните, что вам не нужно # включать заголовок в другой заголовок, если у вас есть только указатель или ссылка. Например.:

Требуется заголовок:

#include "Y.h"
class X
{
   Y y; // need header for Y
};

Заголовок не требуется:

class Y; 
class X
{
   Y* y; // don't need header for Y
};
//#include "Y.h" in .cpp file

Второй пример компилируется быстрее и имеет меньше зависимостей. Это может быть важно в больших базах кода.

Спасибо, это так называемое форвардное объявление?

MainID 21.01.2009 23:01

Ага, это предварительное заявление. Очень полезно для ускорения компиляции. Это работает только тогда, когда компилятору не нужны детали; вы не можете использовать его для базовых классов, реальных членов и тому подобного.

David Thornley 22.01.2009 01:05

Проверьте Дискуссия при использовании #include<filename.h> а #include<filename> для C++ включает библиотеки C.

Обновлено: Энди Брайс также кратко высказался об этом.

Следуя нулевому ответу, самое важное, о чем нужно подумать, - это то, где вы помещаете свои # include.

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

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

По возможности лучше использовать форвардные объявления, чтобы вы могли записать сигнатуры методов и т. д., А затем # включить соответствующие файлы в файл .cpp, чтобы вы могли фактически использовать классы и структуры, от которых зависит ваш код.

//In myclass.h
class UtilClass; //Forward declaration of UtilClass - avoids having to #include untilclass.h here

class MyClass
{
    MyClass();
    ~MyClass();

    void DoSomethingWithUtils(UtilClass *util); //This will compile due to forward declaration above
};

//Then in the .cpp
#include utilclass.h

void MyClass::DoSomethingWithUtils(UtilClass *util)
{
    util->DoSomething(); //This will compile, because the class definition is included locally in this .cpp file.
}
Это будет компилироваться, потому что [...] <- Я не думаю, что это действительно полезный комментарий: нужно много работать, чтобы объяснить, почему код компилируется. В вашем конкретном случае это также неправильный, потому что компилятор, вероятно, остановится уже на #include utilclass.h;), так что вам лучше попробовать по-настоящему скомпилировать это раньше ...
Wolf 13.12.2017 11:51

Таким образом, ваш компилятор может поддерживать 2 уникальных пути поиска для включаемых файлов:
Неформально мы могли бы назвать систему include path, а пользователя include path. #include <XX> ищет системный путь включения. #include "XX" ищет путь включения пользователя, затем путь включения системы.

Проверка проекта стандарта n2521:
Раздел 16.2:

2 A preprocessing directive of the form 

  # include < h-char-sequence> new-line 

  searches a sequence of implementation-defined places for a header identified
  uniquely by the specified sequence between the < and > delimiters, and
  causes the replacement of that directive by the entire contents of the
  header. How the places are specified or the header identified is
  implementation-defined. 

3 A preprocessing directive of the form 

  # include " q-char-sequence" new-line 

  causes the replacement of that directive by the entire contents of the
  source file identified by the specified sequence between the " " delimiters.
  The named source file is searched for in an implementation-defined manner.
  If this search is not supported, or if the search fails, the directive is
  reprocessed as if it read

  # include < h-char-sequence> new-line 

  with the identical contained sequence (including > characters, if any)
  from the original directive. 

Примером этого может быть gcc

  -isystem <dir>              Add <dir> to the start of the system include path
  -idirafter <dir>            Add <dir> to the end of the system include path
  -iwithprefix <dir>          Add <dir> to the end of the system include path
  -iquote <dir>               Add <dir> to the end of the quote include path
  -iwithprefixbefore <dir>    Add <dir> to the end of the main include path
  -I <dir>                    Add <dir> to the end of the main include path

Чтобы узнать, где находится ваш gcc, сделайте следующее:

g++ -v -E -xc++ /dev/null -I LOOK_IN_HERE
#include "..." search starts here:
#include <...> search starts here:
  LOOK_IN_HERE
  /usr/include/c++/4.0.0
  /usr/include/c++/4.0.0/i686-apple-darwin9
  /usr/include/c++/4.0.0/backward
  /usr/local/include
  /usr/lib/gcc/i686-apple-darwin9/4.0.1/include
  /usr/include
  /System/Library/Frameworks (framework directory)
  /Library/Frameworks (framework directory)
End of search list.

Итак, как использовать эти знания. Есть несколько школ мысли. Но я всегда перечисляю свои библиотеки от самых частных до самых общих.

Пример

Файл: plop.cpp

#include "plop.h"
#include "plop-used-class.h"

/// C Header Files
#include <stdio.h>    // I know bad example but I drew a blank

/// C++ Header files
#include <vector>
#include <memory>

Таким образом, если в заголовочный файл "plop-used-class.h" должен быть включен <vector>, это будет обнаружено компилятором. Если бы я поместил <вектор> вверху, эта ошибка была бы скрыта от компилятора.

... какие компиляторы поддерживают различие системный путь vs. путь пользователя? Похоже, вы имеете в виду исключительно GCC ...

Wolf 12.12.2017 17:48

@Wolf Я процитировал стандарт (как это было в 09). Что говорит о том, как файл найден, это implementation-defined. Затем я покажу, как реализация gcc в качестве «примера» (gcc является доминирующим компилятором C++ в то время, clang существует, но едва ли можно использовать в 09, не относящемся к 2011 году, компилятор MS cl, похоже, имеет только один путь поиска).

Martin York 12.12.2017 20:05

Ясно спасибо. Итак, вы перевели места, определяемые реализацией в система включает путь.

Wolf 12.12.2017 20:12

@Wolf Помимо ответа на вопрос именно цитатой из стандарта. Затем я проиллюстрировал реальный пример того, как реализация интерпретирует implementation-defined для решения реальной ситуации.

Martin York 12.12.2017 21:56

Просто дополнение к ответу Энди Брайса, вы также можете обойтись предварительными объявлениями для возвращаемых значений функций:

class Question;
class Answer;

class UniversityChallenge
{
...
    Answer AskQuestion( Question* );
...
};

Вот ссылка на вопрос, который я задал некоторое время назад, с хорошими ответами http://bytes.com/groups/c/606466-forward-declaration-allowed.

Is there any material about how to use #include correctly?

Я настоятельно рекомендую раздел SF: Исходные файлыОсновные принципы C++ в качестве хорошей отправной точки.

I didn't find any C/C++ text book that explains this usage in detail.

Многие общепринятые представления о физическом составе проектов C++, вероятно, можно найти в «Крупномасштабная разработка программного обеспечения на C++» Джона Лакоса.

In formal project, I always get confused in dealing with it.

Вы в хорошей компании. До Модули C++ 20#include был единственным практическим способом составления единиц перевода C++ из нескольких файлов. Это простая, ограниченная возможность, с помощью которой препроцессор по существу копирует / вставляет целые файлы в другие файлы. Результирующий ввод компилятора часто бывает огромным, и работа обычно повторяется от одной единицы трансляции к другой.

Другие вопросы по теме