Для чого потрібні Header файли в С++? Чому не можна писати без них?
Що заважає підключати просто .cpp
файли?
Ну підключив ти його два рази, ну нехай компілятор тільки один раз підключає і все, раз він все в один як би файл склеює, значить перше підключення буде вище за інших і буде видно внизу. Нехай дивиться час зміни і перекомпілюються тільки те що змінилося. Нехай автоматично генерує і прикріплює header файли з описом інтерфейсів до відкомпільованого бінарнику і т.п.
Іншими словами, чому рутинна робота по генерації хедер файлів, не автоматизована і покладена на розробника? Адже компілятор поруч з відкомпільованим бінарники може легко сам згенерувати файл опису інтерфейсів (який він отримав, скануючи cpp файл).
Можете навести ситуацію, в якій би виникали ПРОБЛЕМИ без використання хедер файлів? Це буде найкраще пояснення.
Відповіді на питання (1)
Проблема лежить в області забезпечення зворотної сумісності.
Подивіться, будь-яку новий мову програмування - наприклад Паскаль, не кажучи вже про Java або C # - не потребують заголовних файлах. Без сумніву, C++ теж міг би обійтися без них. У чому ж справа?
Перенесемося на півстоліття назад, в 1972 рік. Уявімо собі компілятор мови C.
Припустимо, ми хочемо написати дизайн компілятора. Ми не можемо скомпілювати всю програму за раз, у нас на це просто не вистачить пам'яті. Комп'ютери тоді були маленькими і повільними. Ми хочемо компілювати програму по шматочках, по кілька функцій за раз.
У нас відразу ж виникає проблема: як скомпілювати функцію f
, яка посилається на іншу функцію g
? Нам потрібно окремий опис інших функцій. Ми могли б, звичайно, прочитати всі вихідні файли, для початку з'ясувати, які функції у нас є, і потім прочитати їх вдруге і скомпілювати один за одним. Але це було занадто складно і повільно, потрібно було парсити визначення функцій двічі, і один раз викидати результат! Це неприпустима витрата процесорного часу! Плюс, якщо тримати в пам'яті визначення всіх функцій, може знову-таки не вистачити пам'яті.
На кого Денніс вирішив покласти складну проблему відділення опису функції від її реалізації, і підключення тільки потрібних описів при компіляції даної функції? На нас, програмістів. Він вирішив, що ми повинні самі допомогти компілятору і скопіювати визначення функцій в окремий файл, і самі вказати компілятору, які файли з визначеннями потрібні. (Тобто перший крок компіляції покладається на нас.)
Це радикально спростило компілятор, але привело в свою чергу до проблем. Що буде, якщо ми забули підключити потрібні заголовки? Відповідь: помилка компіляції. Що буде, якщо зміст тексту заголовки змінюється в залежності від якого-небудь макросу? Відповідь: компілятор «тупий», і не намагається детектувати цю проблему, він перекладає відповідальність на нас.
На момент розробки мови це було правильне рішення. Компілятор виявився практичним, швидким, і програмісти були не проти допомогти компіляції. Ну і якщо хто припускався помилки, сам був винен.
Перемотати стрілки годинника в 1983 рік. Бьярн створює C++. Він вирішив злетіти на хвилі популярності мови C, і перейняв модель компіляції C з окремими translation unit'ами та пов'язаними з цим проблемами прямо з C. Втім, перші версії C++ були просто препроцесором мови C! Тому проблеми роздільної компіляції перекочували з C в C++. Більше того, додалися нові проблеми. Наприклад, шаблони класів виглядають як класи, але не генерують об'єктного коду самі по собі, тому для них доводиться йти на хитрощі і обходити недоліки системи роздільної компіляції (наприклад, включенням реалізації в header і трюками компоновщика).
А далі вступила в гру зворотна сумісність. Зараз, в 2017 році, ми маємо так багато коду, написаного в стилі «з заголовками», і так багато коду виходить з різних тонкощів, пов'язаних з цим, що міняти парадигму вже пізно, поїзд практично поїхав.
Втім, існує проект модульної системи в C++, який повинен допомогти програмістам позбутися спадщини півстолітньої давності. Він ще не реалізований, і в ньому є складнощі рівня дизайну (наприклад, в header'і був визначений макрос, чи буде його видно, якщо ми перейдемо від header'ів до модулів?) Сподіваюся, в майбутньому розробники мови таки зможуть побороти проблему зворотної сумісності.