Чому компілятор не може не імпортувати файл заголовка двічі самостійно?


13

Нове для C ++! Тому я читав це: http://www.learncpp.com/cpp-tutorial/110-a-first-look-at-the-preprocessor/

Охоронці заголовків

Оскільки файли заголовків можуть включати інші файли заголовків, можна опинитися в ситуації, коли файл заголовка потрапляє кілька разів.

Тому ми робимо директиви препроцесорів, щоб цього уникнути. Але я не впевнений - чому компілятор не може просто ... не імпортувати те саме два рази?

Враховуючи, що захисні заголовки необов’язкові (але, мабуть, хороша практика), це майже змушує мене думати, що існують сценарії, коли ви хочете щось імпортувати двічі. Хоча я взагалі не можу придумати жодного такого сценарію. Будь-які ідеї?


У компіляторі MS є #pragma onceякий повідомляє компілятору включити цей файл лише один раз.
CodesInChaos

Відповіді:


27

Вони можуть, як показали нові мови.

Але рішення дизайну було прийнято всі ті роки тому (коли компілятор C був декількома незалежними етапами), і тепер для підтримки сумісності попередній процесор повинен діяти певним чином, щоб переконатися, що старий компілюється код, як очікувалося.

Оскільки C ++ успадковує спосіб обробки файлів заголовків із C, він підтримував ті самі методи. Ми підтримуємо старе дизайнерське рішення. Але змінити спосіб його роботи надто ризиковано, багато коду може потенційно зламатись. Тож тепер ми маємо навчити нових користувачів мови, як користуватися, включаючи охорону.

Є кілька хитрощів із файлами заголовків, якщо ви навмисно включали їх кілька разів (це насправді дає корисну функцію). Хоча якби ми переробили парадигму з нуля, ми могли б зробити це не за замовчуванням способом включення файлів.


7

Інакше це не буде настільки виразно, враховуючи, що вони вирішили підтримувати сумісність із C і, таким чином, продовжувати працювати з препроцесором, а не з традиційною системою упаковки.

Одне, що мені спадає на думку, це те, що у мене був проект, який був API. У мене було два файли заголовків x86lib.hі x86lib_internal.h. Оскільки внутрішній був величезним, я розділив "загальнодоступні" біти на x86lib.h, щоб користувачам не потрібно було відкладати додатковий час для збирання.

Це створило смішну проблему із залежностями, хоча я в кінцевому підсумку мав такий потік, який пройшов так у x86lib_internal

  1. Встановіть значення INTERNAL препроцесора
  2. Включіть x86lib.h (який розумно діяти певним чином, коли було визначено внутрішнє)
  3. Виконайте деякі речі та введіть деякі речі, які використовуються в x86lib.h
  4. Встановити ПІСЛЯ визначення препроцесора
  5. Знову включіть x86lib.h (цього разу він ігнорував би все, крім окремої частини ПІСЛЯ, яка залежала від елементів x86lib_internal

Я б не сказав, що це був найкращий шлях для цього, але він досяг того, що хотів.


0

Одна з труднощів з автоматичним виключенням дублікатів заголовка полягає в тому, що стандарт C відносно беззвучний щодо того, що означає назви файлів. Наприклад, припустимо , що основний файл компілюється містить директиви #include "f1.h"і #include "f2.h", і знайдені файли для цих директив і містять #include "f3.h". Якщо f1.hі f2.hзнаходяться в різних каталогах, але вони були знайдені шляхом пошуку включають шляхи, то було б незрозуміло, що #includeдирективи в межах цих файлів повинні були завантажувати один і той же f3.hфайл або різні.

Справа стає ще гіршою, якщо додати можливості включення файлів, включаючи відносні шляхи. У деяких випадках, коли файли заголовків використовують відносні шляхи для вкладених файлів, включають директиви, і де потрібно, щоб уникнути будь-яких змін у наданих файлах заголовків, можливо, буде потрібно дублювати файл заголовка в декількох місцях у структурі каталогів проекту. Незважаючи на те, що існує декілька фізичних копій цього заголовкового файлу, їх слід розцінювати семантично так, ніби вони є одним файлом.

Якщо #pragma onceдиректива дозволила слідувати ідентифікатору onceіз семантикою, за якою компілятор повинен пропустити файл, якщо ідентифікатор збігається з попередньою #pragma onceдирективою, то семантика була б однозначною; компілятор, який міг би сказати, що #includeдиректива завантажує той самий #pragma onceтег-файл, що і попередній, це могло б заощадити трохи часу, пропустивши файл, не відкриваючи його знову, але таке виявлення не було б семантично важливим, оскільки файл буде пропущений чи чи ні ім'я файлу було визнано збігом. Я не знаю, що жоден компілятор працює таким чином. Маючи компілятор, спостерігайте, чи файл відповідає шаблону, #ifndef someIdentifier / #define someIdentifier / #endif [for that ifndef] / nothing followingі трактує таку річ, як рівнозначну вище, #pragma once someIdentifierякщоsomeIdentifier залишається визначеним, по суті так само добре.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.