Я считаю, что заголовочные файлы полезны при просмотре исходных файлов C ++, потому что они дают «сводку» всех функций и членов данных в классе. Почему многие другие языки (такие как Ruby, Python, Java и т. Д.) Не имеют такой функции? Является ли это областью, где многословие C ++ пригодится?
20
Ответы:
Первоначальная цель заголовочных файлов состояла в том, чтобы разрешить однопроходную компиляцию и модульность в C. Объявляя методы перед их использованием, он допускал только один проход компиляции. Эта эпоха давно прошла благодаря нашим мощным компьютерам, способным выполнять многопроходную компиляцию без каких-либо проблем, а иногда даже быстрее, чем компиляторы C ++.
C ++ для обратной совместимости с C требовала сохранения заголовочных файлов, но добавляла много поверх них, что приводило к довольно проблематичному дизайну. Больше в FQA .
Для модульности заголовочные файлы были необходимы в качестве метаданных о коде в модулях. Например. какие методы (и в классах C ++) доступны в какой библиотеке. Было очевидно, что разработчик написал это, потому что время компиляции было дорогим. В настоящее время нет проблем с тем, чтобы компилятор генерировал эти метаданные из самого кода. Языки Java и .NET делают это нормально.
Так что нет. Заголовочные файлы не хороши. Они были, когда нам все еще нужно было иметь компилятор и компоновщик на отдельных дискетах, и компиляция заняла 30 минут. В настоящее время они только мешают и являются признаком плохого дизайна.
источник
Хотя они могут быть полезны для вас как форма документации, система окружающих заголовочных файлов чрезвычайно неэффективна.
C был спроектирован так, чтобы каждый проход компиляции строил один модуль; каждый исходный файл компилируется в отдельном прогоне компилятора. Заголовочные файлы, с другой стороны, вставляются в этот этап компиляции для каждого из исходных файлов, которые ссылаются на них.
Это означает, что если ваш заголовочный файл включен в 300 исходных файлов, то он анализируется и компилируется снова и снова, 300 раз во время сборки вашей программы. Одна и та же вещь с одним и тем же результатом, снова и снова. Это огромная трата времени и одна из главных причин, почему программы на C и C ++ так долго создаются.
Все современные языки намеренно избегают этой абсурдной неэффективности. Вместо этого, как правило, в скомпилированных языках необходимые метаданные хранятся в выходных данных сборки, что позволяет скомпилированному файлу действовать как своего рода справочник по быстрому поиску, описывающий, что содержит скомпилированный файл. Все преимущества заголовочного файла, автоматически создаваемого без дополнительной работы с вашей стороны.
Поочередно в интерпретируемых языках каждый загружаемый модуль остается в памяти. Ссылка или включение или требование какой-либо библиотеки приведет к чтению и компиляции соответствующего исходного кода, который остается резидентным до завершения программы. Если вам также требуется это в другом месте, никаких дополнительных работ, так как он уже загружен.
В любом случае вы можете «просматривать» данные, созданные на этом шаге, используя языковые инструменты. Обычно в среде IDE используется браузер классов. И если у языка есть REPL, его также часто можно использовать для генерации сводной документации по любым загруженным объектам.
источник
Неясно, что вы подразумеваете под просмотром файла для функций и членов данных. Однако большинство IDE предоставляют инструменты для просмотра класса и просмотра членов класса.
Например: Visual Studio имеет,
Class View
иObject browser
это приятно обеспечивает запрошенную функциональность. Как на следующих скриншотах.источник
Дополнительным недостатком заголовочных файлов является то, что программа зависит от порядка их включения, и программист должен предпринять дополнительные шаги для обеспечения корректности.
Это в сочетании с макросистемой C (унаследованной от C ++) приводит ко многим подводным камням и запутанным ситуациям.
Чтобы проиллюстрировать, что если один заголовок определил некоторый символ с использованием макросов для его использования, а другой заголовок использовал символ другим способом, таким как имя для функции, то порядок включения сильно повлияет на конечный результат. Таких примеров много.
источник
Мне всегда нравились заголовочные файлы, так как они предоставляют форму интерфейса для реализации вместе с некоторой дополнительной информацией, такой как переменные класса, все в одном простом для просмотра файле.
Я вижу много кода на C # (для которого не нужно 2 файла на класс), написанного с использованием 2 файлов на класс - один - фактическая реализация класса, а другой - с интерфейсом в нем. Этот дизайн хорош для насмешек (необходим в некоторых системах) и помогает определить документацию класса, не используя IDE для просмотра скомпилированных метаданных. Я бы зашел так далеко, чтобы сказать, что это хорошая практика.
Так что C / C ++ предписывает эквивалент (своего рода) интерфейса в заголовочных файлах - это хорошо.
Я знаю, что есть сторонники других систем, которым они не нравятся по причинам, в том числе «сложнее просто взломать код, если вам нужно поместить вещи в 2 файла», но я считаю, что просто взломать код - это не очень хорошая практика. и как только вы начнете писать / разрабатывать код, немного подумав, вы, конечно же, будете определять заголовки / интерфейсы.
источник
Я бы на самом деле сказал, что файлы заголовков не очень хороши, потому что они запутывают интерфейс и реализацию. Цель программирования в целом, и особенно ООП, состоит в том, чтобы иметь определенный интерфейс и скрывать детали реализации, но в заголовочном файле C ++ показаны как методы, наследование и открытые члены (интерфейс), так и частные методы и частные члены. (некоторая часть реализации). Не говоря уже о том, что в некоторых случаях вы в конечном итоге встраиваете код или конструкторы в заголовочный файл, а некоторые библиотеки включают шаблонный код в заголовки, который действительно смешивает реализацию с интерфейсом.
Я полагаю, что первоначальная цель состояла в том, чтобы позволить коду использовать другие библиотеки, объекты и т. Д. Без импорта всего содержимого скрипта. Все, что вам нужно, это заголовок для компиляции и ссылки. Это экономит время и циклы таким образом. В этом случае это хорошая идея, но это только один из способов решения этих проблем.
Что касается просмотра структуры программы, большинство IDE предоставляют такую возможность, и существует множество инструментов, которые запускают интерфейсы, выполняют анализ кода, декомпиляцию и т. Д., Чтобы вы могли видеть, что происходит под прикрытием.
Что касается того, почему другие языки не реализуют ту же функцию? Ну, потому что другие языки приходят от других людей, и эти дизайнеры / создатели имеют другое видение того, как все должно работать.
Лучший ответ - придерживаться того, что нужно делать, и делать вас счастливыми.
источник
Во многих языках программирования, когда программа подразделяется на несколько модулей компиляции, код в одном модуле сможет использовать вещи, определенные в другом, только если у компилятора есть какие-то средства для того, чтобы знать, что это такое. Вместо того, чтобы требовать, чтобы компилятор, обрабатывающий одну единицу компиляции, исследовал весь текст каждой единицы компиляции, которая определяет все, что используется в данной единице, лучше, чтобы компилятор получал, обрабатывая каждую единицу, полный текст единицы, которая должна быть скомпилирована вместе с небольшой информацией от всех остальных.
В C программист отвечает за создание двух файлов для каждого модуля - один, содержащий информацию, которая понадобится только при компиляции этого модуля, и один, содержащий информацию, которая также понадобится при компиляции других модулей. Это довольно неприятный, хакерский дизайн, но при этом не нужно, чтобы языковой стандарт определял что-либо о том, как компиляторы должны генерировать и обрабатывать любые промежуточные файлы. Многие другие языки используют другой подход, при котором компилятор генерирует из каждого исходного файла промежуточный файл, описывающий все в этом исходном файле, который должен быть доступен для внешнего кода. Этот подход избавляет от необходимости иметь дублирующую информацию в двух исходных файлах, но требует, чтобы платформа компиляции определяла семантику файлов таким образом, который не требуется для C.
источник