Перевантажені тисячами рядків коду?
Наявність одного набору заголовків / вихідних файлів на клас у каталозі може здатися надмірним. І якщо кількість занять сягає 100 або 1000, це може навіть лякати.
Але, погравши з джерелами, дотримуючись філософії «давайте все складемо», висновок такий: лише той, хто написав файл, має надію не загубитися всередині. Навіть з IDE легко пропустити речі, тому що коли ви граєте з джерелом в 20 000 рядків, ви просто закриваєте свою думку на все, що не зовсім стосується вашої проблеми.
Приклад із реального життя: ієрархія класів, визначена в цих тисячах джерел, закрилася у спадщину алмазів, а деякі методи були замінені в дочірніх класах методами з точно однаковим кодом. Це легко було пропустити (хто хоче дослідити / перевірити 20 000 рядків вихідного коду?), І коли початковий метод був змінений (виправлення помилок), ефект був не таким універсальним, як виняток.
Залежності стають круговими?
У мене була проблема з шаблонним кодом, але я бачив подібні проблеми зі звичайним кодом С ++ та С.
Розбиття джерел на 1 заголовок на структуру / клас дозволяє:
- Прискоріть компіляцію, оскільки ви можете використовувати переадресацію символів замість включення цілих об'єктів
- Мати кругові залежності між класами (§) (тобто клас A має вказівник на B, а B має вказівник на A)
У коді, керованому джерелом, залежності класів можуть призвести до регулярного переміщення класів вгору та вниз по файлу, лише щоб змусити компіляцію заголовка. Ви не хочете вивчати розвиток таких кроків, порівнюючи один і той же файл у різних версіях.
Наявність окремих заголовків робить код більш модульним, швидшим для компіляції та полегшує вивчення його розвитку через різні версії.
Для моєї програми-шаблону мені довелося розділити свої заголовки на два файли: файл .HPP, що містить декларацію / визначення класу шаблону, та файл .INL, що містить визначення згаданих методів класу.
Поміщення всього цього коду всередину одного і тільки одного унікального заголовка означало б розміщення визначень класів на початку цього файлу, а визначень методів в кінці.
І тоді, якщо комусь потрібна лише невелика частина коду, з рішенням лише з одним заголовком, їм все одно доведеться платити за повільнішу компіляцію.
(§) Зверніть увагу, що ви можете мати кругові залежності між класами, якщо знаєте, який клас кому належить. Це дискусія щодо класів, які знають про існування інших класів, а не про кругові залежності anti_ Pattern.
Останнє слово: заголовки повинні бути самодостатніми
Однак одне, що повинно дотримуватися рішення кількох заголовків та кількох джерел.
Коли ви включаєте один заголовок, незалежно від того, який заголовок, ваше джерело має компілювати чисто.
Кожен заголовок повинен бути самодостатнім. Ви повинні розробити код, а не шукати скарби, зігріваючи свій проект із понад 10 000 вихідних файлів, щоб знайти, який заголовок визначає символ у заголовку з 1000 рядків, який потрібно включити лише через одне перерахування.
Це означає, що кожен заголовок визначає або оголошує вперед усі символи, які він використовує, або включає всі необхідні заголовки (і лише необхідні заголовки).
Питання про кругові залежності
підкреслення-d запитує:
Чи можете ви пояснити, як використання окремих заголовків має якесь значення для кругових залежностей? Я не думаю, що це так. Ми можемо тривіально створити циклічну залежність, навіть якщо обидва класи повністю оголошені в одному заголовку, просто попередньо оголосивши один заздалегідь, перш ніж оголосити дескриптор для нього в іншому. Все інше здається чудовими моментами, але думка, що окремі заголовки полегшують кругові залежності, здається далекою
underscore_d, 13 листопада о 23:20
Скажімо, у вас є 2 шаблони класів, A та B.
Скажімо, визначення класу A (відповідно B) має вказівник на B (відповідно A). Скажімо також, що методи класу A (відповідно B) насправді викликають методи з B (відповідно A).
Ви маєте кругову залежність як у визначенні класів, так і в реалізації їх методів.
Якби A і B були нормальними класами, а методи A і B знаходились у файлах .CPP, не виникало б жодних проблем: ви б використовували переадресоване оголошення, мали заголовок для кожного визначення класу, тоді кожен CPP містив би обидва HPP.
Але оскільки у вас є шаблони, ви насправді повинні відтворити ці шаблони вище, але лише із заголовками.
Це означає:
- заголовки визначення A.def.hpp та B.def.hpp
- заголовок реалізації A.inl.hpp та B.inl.hpp
- для зручності "наївний" заголовок A.hpp та B.hpp
Кожен заголовок матиме такі риси:
- В A.def.hpp (відповідно B.def.hpp) у вас є переадресація класу B (відповідно A), що дозволить вам оголосити вказівник / посилання на цей клас
- A.inl.hpp (відповідно B.inl.hpp) включатиме як A.def.hpp, так і B.def.hpp, що дозволить методам з A (відповідно B) використовувати клас B (відповідно A) .
- A.hpp (відповідно B.hpp) безпосередньо включатиме як A.def.hpp, так і A.inl.hpp (відповідно B.def.hpp і B.inl.hpp)
- Звичайно, всі заголовки повинні бути самодостатніми та захищені захисними колонтитулами
Наївний користувач включатиме A.hpp та / або B.hpp, ігноруючи таким чином весь безлад.
А наявність такої організації означає, що програма для бібліотеки може вирішувати кругові залежності між A і B, зберігаючи обидва класи в окремих файлах, легко орієнтуючись, як тільки ви зрозумієте схему.
Зверніть увагу, що це був крайній випадок (два шаблони, що знають один одного). Я сподіваюся, що більшість кодів не потребують цього фокусу.