Страницы

Поиск по вопросам

понедельник, 1 октября 2018 г.

Для чего нужны Header файлы в С++? Почему нельзя писать без них?

Весь гугл перерыл, не могу понять. И википедию перечитал и вообще все что угодно перечитал. Правда не понимаю.
Что мешает подключать просто .cpp файлы?
Ну подключил ты его два раза, ну пусть компилятор только один раз подключает и все, раз он один черт все в один как бы файл склеивает, значит первое подключение будет выше остальных и будет видно им внизу. Пусть смотрит время изменения и перекомпилирует только изменившееся. Пусть автоматически генерирует и прикрепляет header файлы с описанием интерфейсов к откомпилированному бинарнику и т.п.
Иными словами, почему рутиная работа по генерации хедер файлов, не автоматизирована и возложена на разработчика? Ведь компилятор рядом с откомпилированным бинарником может легко сам сгенерировать файл описания интерфейсов (который он получил, сканируя cpp файл).
Можете привести ситуацию, в которой бы возникали ПРОБЛЕМЫ без использования хедер файлов? Это будет самое лучшее обьяснение.


Ответ

Проблема лежит в области обратной совместимости.
Посмотрите, любой новый язык программирования — да даже Паскаль, не говоря уже о Java или C# — не нуждаются в заголовочных файлах. Без сомнения, C++ тоже мог бы обойтись без них. В чём же дело?
Перенесёмся на полвека назад, в 1972 год. Представим себе компилятор языка C.
Допустим, мы хотим написать дизайн компилятора. Мы не можем скомпилировать всю программу за раз, у нас на это просто не хватит памяти. Компьютеры тогда были маленькими и медленными. Мы хотим компилировать программу по кусочкам, по нескольку функций за раз.
У нас сразу же возникает проблема: как скомпилировать функцию f, которая ссылается на другую функцию g? Нам нужно отдельное описание других функций. Мы могли бы, конечно, прочитать все исходные файлы, для начала выяснить, какие функции у нас есть, и затем прочитать их второй раз и скомпилировать один за одним. Но это было слишком сложно и медленно, нужно было парсить определения функций дважды, и один раз выбрасывать результат! Это недопустимый расход процессорного времени! Плюс если держать в памяти определения всех функций, может снова-таки не хватить памяти.
На кого Деннис решил возложить сложную проблему отделения описания функции от её реализации, и подключения только нужных описаний при компиляции данной функции? На нас, программистов. Он решил, что мы должны сами помочь компилятору и скопипастить определения функций в отдельный файл, и сами указать компилятору, какие файлы с определениями нужны. (То есть первый шаг компиляции возлагается на нас.)
Это радикально упростило компилятор, но привело в свою очередь к проблемам. Что будет, если мы забыли подключить нужный заголовочный файл? Ответ: ошибка компиляции. Что будет, если смысл текста заголовочного файла меняется в зависимости от какого-нибудь макроса? Ответ: компилятор «тупой», и не пытается детектировать эту проблему, он перекладывает ответственность на нас.
На момент разработки языка это было правильное решение. Компилятор оказался практичным, быстрым, и программисты были не прочь помочь компиляции. Ну и если кто допускал ошибку, сам был виноват.
Перемотаем стрелки часов в 1983 год. Бьярн создаёт C++. Он решил взлететь на волне популярности языка C, и перенял модель компиляции C с отдельными translation unit'ами и связанными с этим проблемами прямо из C. Впрочем, первые версии C++ были просто препроцесоором языка C! Поэтому проблемы раздельной компиляции перекочевали из C в C++. Хуже того, добавились новые проблемы. Например, шаблоны классов выглядят как классы, но не генерируют объектного кода сами по себе, поэтому для них приходится идти на ухищрения и обходить недостатки системы раздельной компиляции (например, включением реализации в header и трюками компоновщика).
А дальше вступила в игру обратная совместимость. Сейчас, в 2017 году, есть так много кода, написанного в стиле «с заголовками», и так много кода исходит из различных тонкостей, связанных с этим, что менять парадигму уже поздно, поезд практически уехал.
Впрочем, существует проект модульной системы в C++, который должен помочь программистам избавиться от наследства полувековой давности. Он ещё не реализован, и в нём есть сложности уровня дизайна (например, в header'е был определён макрос, будет ли он виден, если мы перейдём от header'ов к модулям?) Надеюсь, в будущем разработчики языка таки смогут побороть проблему обратной совместимости.

Комментариев нет:

Отправить комментарий