Який порядок повинен містити вказані файли, тобто які причини включення одного заголовка до іншого?
Наприклад, чи йдуть системні файли, STL та Boost до або після того, як локальні включають файли?
Який порядок повинен містити вказані файли, тобто які причини включення одного заголовка до іншого?
Наприклад, чи йдуть системні файли, STL та Boost до або після того, як локальні включають файли?
Відповіді:
Я не думаю, що є рекомендоване замовлення, поки воно складається. Що дратує, коли одні заголовки вимагають, щоб перші заголовки були включені першими ... Це проблема з самими заголовками, а не з порядком включень.
Мої особисті переваги - перейти від локального до глобального, кожен підрозділ в алфавітному порядку, тобто:
Моє обґрунтування 1. полягає в тому, що він повинен довести, що кожен заголовок (для якого є cpp) може бути #include
d без передумов (terminus technicus: заголовок "автономний"). А решта просто схоже логічно витікає звідти.
Варто пам’ятати, що ваші заголовки не повинні залежати від того, щоб перші заголовки були включені першими. Один із способів убезпечити це - включити заголовки до інших заголовків.
Зокрема, про це згадує "Думаючи про C ++", посилаючись на "Великий масштаб C ++ Software Design" Лакоса:
Приховані помилки використання можна уникнути, переконавшись, що .h файл компонента сам розбирає - без наданих зовні декларацій чи визначень ... У тому числі .h файл як перший рядок файлу .c забезпечує відсутність критичного фрагмента Інформація, властива фізичному інтерфейсу компонента, відсутня у файлі .h (або, якщо є, ви дізнаєтесь про нього, як тільки спробуєте зібрати файл .c).
Тобто, включіть у такому порядку:
Якщо будь-який із заголовків має проблеми з включенням до цього наказу, виправте їх (якщо ваш) або не використовуйте їх. Бойкот бібліотек, які не пишуть чисті заголовки.
Посібник зі стилів Google C ++ стверджує майже зворотне, насправді зовсім не виправдання; Я особисто схильний до підходу Лакоса.
Я дотримуюся двох простих правил, які уникають переважної більшості проблем:
Я також дотримуюся вказівок:
Іншими словами:
#include <stdio.h>
#include <string.h>
#include "btree.h"
#include "collect_hash.h"
#include "collect_arraylist.h"
#include "globals.h"
Хоча, будучи настановами, це суб’єктивна річ. Правила з іншого боку, я суворо виконую, навіть до того, щоб забезпечити "загорткові" файли заголовків з включенням охоронців і згрупованих включає, якщо якийсь недобрий сторонній розробник не підписався на моє бачення :-)
Щоб додати до стіни власну цеглу.
Тому я зазвичай так:
// myproject/src/example.cpp
#include "myproject/example.h"
#include <algorithm>
#include <set>
#include <vector>
#include <3rdparty/foo.h>
#include <3rdparty/bar.h>
#include "myproject/another.h"
#include "myproject/specific/bla.h"
#include "detail/impl.h"
Кожна група відокремлена порожнім рядком від наступної:
Також зауважте, що, крім системних заголовків, кожен файл знаходиться у папці з назвою простору імен, лише тому, що їх простежити таким чином простіше.
#define
сприймає інший код) та запобігає неявним залежностям. Наприклад, якщо foo.h
насправді залежить наш заголовок коду базового коду, <map>
але скрізь, де він був використаний у .cc
файлах, <map>
трапилось, що він уже включений, ми, ймовірно, не помітимо. Поки хтось не спробував включити, попередньо foo.h
не включаючи <map>
. І тоді вони будуть роздратовані.
.h
має принаймні один, .cpp
який включає його першим (дійсно, в моєму особистому коді тест Unit, пов’язаний з ним, включає його першим, а вихідний код включає його у свою правильну групу ). Що стосується впливу, якщо будь-який із заголовків включає <map>
будь-які заголовки, включені після цього, все одно впливають, тому для мене це здається програшним боєм.
Header corresponding to this cpp file first (sanity check)
. Чи є щось особливе, якщо #include "myproject/example.h"
переміщено до кінця всіх включених?
Я рекомендую:
І звичайно, в алфавітному порядку в кожному розділі, де це можливо.
Завжди використовуйте прямі декларації, щоб уникнути зайвих #include
s у файлах заголовка.
Я майже впевнений, що це не рекомендована практика в будь-якому місці розумного світу, але мені подобається, що система рядків включає в себе довжину імені файлів, відсортовану за лексикою в межах однієї довжини. Так:
#include <set>
#include <vector>
#include <algorithm>
#include <functional>
Я думаю, що це гарна ідея включити власні заголовки перед іншими народами, щоб уникнути ганьби залежності від включення порядку.
windows.h
.
Це не суб'єктивно. Переконайтеся, що ваші заголовки не покладаються на те, щоб бути #include
d у визначеному порядку. Ви можете бути впевнені, що це не має значення, до якого порядку ви додаєте заголовки STL або Boost.
По- перше , включають заголовок , відповідний .cpp ... Іншими словами, source1.cpp
повинна включати в себе , source1.h
перш ніж включати що - небудь ще. Єдиний виняток, про який я можу подумати - це використання MSVC з попередньо складеними заголовками, і в цьому випадку ви змушені включати stdafx.h
раніше всього іншого.
Обґрунтування: Включення source1.h
до будь-яких інших файлів гарантує, що він може стояти самостійно без залежностей. Якщо source1.h
пізніше набуде залежність, компілятор негайно попередить вас додати необхідні форвардні декларації до source1.h
. Це в свою чергу гарантує, що заголовки можуть бути включені в будь-який порядок залежними від них.
Приклад:
джерело1.h
class Class1 {
Class2 c2; // a dependency which has not been forward declared
};
source1.cpp
#include "source1.h" // now compiler will alert you saying that Class2 is undefined
// so you can forward declare Class2 within source1.h
...
Користувачі MSVC: Настійно рекомендую використовувати попередньо складені заголовки. Отже, перемістіть усі #include
директиви для стандартних заголовків (та інших заголовків, які ніколи не зміняться) на stdafx.h
.
Включіть від найбільш конкретного до найменш конкретного, починаючи з відповідного .hpp для .cpp, якщо такий існує. Таким чином, будуть виявлені будь-які приховані залежності у файлах заголовків, які не є самодостатніми.
Це ускладнюється використанням попередньо складених заголовків. Одним із способів цього є, не роблячи конкретного компілятора проекту, використовувати один із заголовків проекту в якості файлу заздалегідь складеного заголовка.
Це важке запитання у світі C / C ++, у якому стільки елементів перевищує стандарт.
Я думаю, що порядок заголовка файлів не є серйозною проблемою, доки він збирається, як це сказав.
Мої ідеї такі: якщо в усіх цих заголовках немає конфлікту символів, будь-який порядок є нормальним, і питання залежності заголовка можна виправити пізніше, додавши #include рядки до несправного .h.
Справжній клопот виникає, коли якийсь заголовок змінює свою дію (перевіряючи #if умови) відповідно до того, які заголовки вище.
Наприклад, у stddef.h у VS2005 є:
#ifdef _WIN64
#define offsetof(s,m) (size_t)( (ptrdiff_t)&(((s *)0)->m) )
#else
#define offsetof(s,m) (size_t)&(((s *)0)->m)
#endif
Тепер проблема: Якщо у мене є власний заголовок ("custom.h"), який потрібно використовувати з багатьма компіляторами, включаючи деякі старіші, які не містять offsetof
у своїх заголовках системи, я повинен написати у своєму заголовку:
#ifndef offsetof
#define offsetof(s,m) (size_t)&(((s *)0)->m)
#endif
І не забудьте повідомити користувачеві #include "custom.h"
після всіх заголовків системи, інакше рядок offsetof
в stddef.h стверджує про помилку перегляду макросу.
Ми молимось не зустрічати більше таких випадків у своїй кар’єрі.