2009-05-17 1 views
14

Мне просто интересно, в чем смысл разделения классов на файлы .h и .cpp? Это затрудняет редактирование, и если ваш класс не будет скомпилирован в .lib или .dll для внешнего использования, в чем смысл?Зачем ставить объявление класса и определение в двух отдельных файлах на C++?

Редактировать: Причина, по которой я спрашиваю, заключается в том, что библиотеки Boost помещают все в файл .hpp (большинство из них в любом случае), и я хотел знать, почему он разделен в большинстве других кодов, Понимаю.

ответ

15

В C++ есть что-то, называемое Правилом одного определения. Это означает, что (исключая встроенные функции) определения могут появляться только в одном блоке компиляции. Поскольку файлы заголовков C++ просто «копируются и вставляются» в каждый включенный файл, теперь вы помещаете определения в несколько мест, если вы просто поместите определения в файлы заголовков.

Конечно, вы можете сказать, почему бы не сделать все встроенным. Ну, если компилятор уважает ваше встроенное предложение, код для длинных функций будет реплицироваться на каждом сайте вызова, делая ваш код чрезмерно большим и, возможно, вызывая проблемы с кешем, и всевозможные ненужные вещи.

С другой стороны, если компилятор не прослушивает вас и ничего не делает, теперь у вас есть 2 проблемы: 1) вы не знаете, какая единица перевода получила определения ваших классов, и 2) компилятор все равно должен пробираться через ваши определения каждый раз, когда вы # включаете их. Более того, нет простого способа убедиться, что вы случайно не определили тот же метод дважды, в двух разных заголовках, иначе.

Вы также получаете проблему с круговой зависимостью. Чтобы класс вызывал метод другого класса, этот класс должен быть объявлен первым. Поэтому, если 2 классам необходимо вызвать методы друг друга, каждый из них должен быть объявлен до того, как он будет определен. Невозможно сделать это с помощью деклараций и определений в одном файле.

Действительно, именно так были созданы язык и парсер. Это боль, но вам просто нужно иметь дело с этим.

0

Потому что в большинстве случаев вы захотите использовать класс где-то помимо файла, в котором вы его реализуете. Если вы делаете всю программу в одном файле, вам не требуется разделение.

Вы почти никогда не хотите писать программу на C++ в одном файле.

+0

Я имел в виду положить все определение класса в .h и включить его, защищая его с защитой заголовка –

+0

Что произойдет, если вы включите этот файл .h в более чем одном месте в одном исполняемом файле? Ans: вы получаете множество определений одних и тех же функций. - Реальный момент здесь заключается в том, что вы можете делать всевозможные дурацкие вещи с помощью правил, но они почти всегда оказываются неработоспособными на практике как обычные способы. –

3

Ну, одним из преимуществ такого кода является то, что он сокращает время компиляции.

Допустим, у вас есть эти файлы на вашем проекте:

  • ах
  • a.cpp
  • b.cpp

Если у вас уже есть a.cpp компилируется в объект файл ao, то если вы включите ah в b.cpp, compilati должен быть быстрее, потому что парсеру не придется иметь дело со всей декларацией/определением a.

2

Потому что даже в пределах вашей DLL другие классы будут использовать ваш класс. Эти файлы должны видеть объявление класса во время компиляции, включая .h. Они не должны видеть это определение или будут несколько определений функций класса.

0

В C++ отдельная компиляция модулей кода (.c или .cpp) требует, чтобы прототипы функций были определены до использования. Если вы хотите использовать классы или методы, определенные где-то в другом месте, вам нужно импортировать файлы .h, чтобы получить их определение. В конце, компоновщик гарантирует, что все обещания, сделанные в файлах .h, могут быть выполнены всеми файлами c/cpp.

Кроме того, он позволяет создавать целые фреймворки, такие как boost только путем определения файлов .h.

+1

Boost в основном содержит заголовочные файлы, поскольку он в значительной степени опирается на шаблоны C++ (что подразумевает, что в его заголовок, например, для шаблона, нужно поместить определение всего класса). Насколько я помню, часть этой структуры имеет файл cpp, тот, который не полагается на C++-шаблоны: например, библиотеку потоков. –

2

Ваше редактирование re: Boost делает важное различие. Классы шаблонов почти всегда определяются в заголовках из-за того, как компилятор и компоновщик работают в текущем стандарте C++. Вы найдете большинство библиотек шаблонов (а не только Boost) в той же причине реализованы в заголовочных файлах.

5

Boost не строит все его код; он встраивает определения шаблонов для классов, которые он ожидает от своих пользователей, например, shared_ptr. Во многих библиотеках есть разделы, которые необходимо скомпилировать отдельно, например boost :: serialization и program_options.

Вложение может иметь серьезные негативные последствия по мере увеличения размера базы кода. Это увеличивает связь между вашими компонентами, не говоря уже о том, чтобы укусить время компиляции (что усиливается по многим другим причинам :). Фактически, у всех ваших переводческих единиц была бы почти полная копия программы, и небольшое изменение заставило бы вас перестроить/повторить все. По некоторым проектам это может занять много-много часов.

Я никогда не замечал, что это труднее редактировать; по моему опыту, это облегчает из-за четкого разделения интерфейса и реализации, и я знаю, какой файл нужно найти, что я ищу.

+0

Хороший ответ! Я бы добавил только, что, к сожалению, разделение интерфейса и реализации не так ясно, как хотелось бы, поскольку часть реализации протекает в заголовок в виде частных полей данных (поскольку в противном случае компилятор не знал бы размер класса). Идиома pimpl используется для решения этой проблемы. –