При поділі коду на кілька файлів саме те, що саме має перейти у .h-файл, а що - у .cpp-файл?
.hpp
файл, а декларації C переходять у .h
файл. Це дуже корисно при змішуванні коду C і C ++ (наприклад, застарілі модулі в C).
При поділі коду на кілька файлів саме те, що саме має перейти у .h-файл, а що - у .cpp-файл?
.hpp
файл, а декларації C переходять у .h
файл. Це дуже корисно при змішуванні коду C і C ++ (наприклад, застарілі модулі в C).
Відповіді:
Файли заголовків ( .h
) призначені для надання інформації, яка буде потрібна у кількох файлах. Такі речі, як декларації класів, прототипи функцій та перерахування, як правило, містяться у заголовкових файлах. Одним словом, "визначення".
Файли коду ( .cpp
) призначені для надання інформації про реалізацію, яку потрібно знати лише в одному файлі. Загалом, тіло функцій та внутрішні змінні, до яких інші модулі не повинні / ніколи не матимуть доступу, - це те, що належить до .cpp
файлів. Словом, "реалізації".
Найпростіше запитання, яке потрібно задати собі, щоб визначити, що належить де "якщо я це зміню, чи доведеться мені змінити код в інших файлах, щоб зробити речі знову компільованими?" Якщо відповідь "так", вона, ймовірно, належить до файлу заголовка; якщо відповідь "ні", вона, ймовірно, належить до файлу коду.
export
). Єдиний шлях №1 - це PIMPL. # 2 буде можливим, якщо export
він підтримується, а можливо, можливо, використовуючи c ++ 0x та extern
шаблони. IMO, файли заголовків у c ++ втрачають значну частину своєї корисності.
Справа в тому, що в C ++ це дещо складніше, ніж в заголовку / організації джерела.
Компілятор бачить один великий джерельний (.cpp) файл із належними заголовками. Вихідний файл - це блок компіляції, який буде зібраний у файл об'єкта.
Оскільки одному блоку компіляції може знадобитися інформація про реалізацію в іншому блоці компіляції. Отже, можна написати, наприклад, реалізацію функції в одному джерелі, і написати декларацію цієї функції в іншому джерелі, що потребує її використання.
У цьому випадку є дві копії однакової інформації. Що це зло ...
Рішення - поділитися деякими деталями. Хоча реалізація повинна залишатися у Джерелі, декларація спільних символів, як-от функцій, або визначення структур, класів, перерахунків тощо, може бути спільним.
Заголовки використовуються для розміщення цих спільних даних.
Перемістіть до заголовка заяви про те, що потрібно поділити між кількома джерелами
У C ++ є деякі інші речі, які можна помістити в заголовок, оскільки їх також потрібно розділити:
Перемістіть до заголовка ВСЕ, що потрібно поділитись, включаючи спільні реалізації
Так. Насправді, існує багато різних речей, які можуть бути всередині "заголовка" (тобто поділяються між джерелами).
Це ускладнюється, а в деяких випадках (кругові залежності між символами) неможливо зберегти його в одному заголовку.
Це означає, що в крайньому випадку у вас можуть бути:
Давайте уявимо, що у нас є шаблонний MyObject. Ми могли б мати:
// - - - - MyObject_forward.hpp - - - -
// This header is included by the code which need to know MyObject
// does exist, but nothing more.
template<typename T>
class MyObject ;
.
// - - - - MyObject_declaration.hpp - - - -
// This header is included by the code which need to know how
// MyObject is defined, but nothing more.
#include <MyObject_forward.hpp>
template<typename T>
class MyObject
{
public :
MyObject() ;
// Etc.
} ;
void doSomething() ;
.
// - - - - MyObject_implementation.hpp - - - -
// This header is included by the code which need to see
// the implementation of the methods/functions of MyObject,
// but nothing more.
#include <MyObject_declaration.hpp>
template<typename T>
MyObject<T>::MyObject()
{
doSomething() ;
}
// etc.
.
// - - - - MyObject_source.cpp - - - -
// This source will have implementation that does not need to
// be shared, which, for templated code, usually means nothing...
#include <MyObject_implementation.hpp>
void doSomething()
{
// etc.
} ;
// etc.
У "реальному житті" це, як правило, менш складно. Більшість кодів матимуть лише просту організацію заголовка / джерела, з деяким вбудованим кодом у джерелі.
Але в інших випадках (шаблонні об'єкти, які знають одне одного), я повинен був мати для кожного об'єкта окремі заголовки оголошень та реалізації, з порожнім джерелом, що включає ці заголовки, лише щоб допомогти мені побачити деякі помилки компіляції.
Ще однією причиною розбиття заголовків на окремі заголовки може стати прискорення компіляції, обмеження кількості символів, розібраних на суворо необхідне, та уникнення непотрібної перекомпіляції джерела, яке піклується лише про пряму декларацію, коли змінилася реалізація вбудованого методу.
Ви повинні зробити свою організацію коду максимально простою і максимально модульною. Покладіть якомога більше у вихідний файл. Розкрийте лише в заголовках те, що потрібно поділитись.
Але в той день, коли ви матимете кругові залежності між шаблонованими об’єктами, не дивуйтесь, якщо ваша організація коду стане дещо «цікавішою», ніж звичайна організація заголовка / джерела ...
^ _ ^
на додаток до всіх інших відповідей, я розповім, що ви не розміщуєте у файлі заголовка:
using
декларація (найпоширеніша істота using namespace std;
) не повинна відображатися у файлі заголовка, оскільки вони забруднюють простір імен вихідного файлу, у який він включений. .
using
для введення речей у глобальний простір імен у заголовку.
static inline
C99 через те, що щось пов'язане з тим, що відбувається при поєднанні внутрішнього зв’язку з шаблонами. Простори імен дозволяють вам "приховувати" функції, зберігаючи зовнішні зв'язки.
Те, що компілюється в ніщо (нульовий двійковий слід), переходить у файл заголовка.
Змінні не складаються в нічого, але декларації типу роблять (тому що вони лише описують, як ведуть себе змінні).
функцій немає, але вбудовані функції роблять (або макроси), оскільки вони виробляють код лише там, де викликаються.
шаблони не є кодом, вони є лише рецептом створення коду. тому вони також ходять у h-файлах.
Загалом, ви розміщуєте декларації у файлі заголовка та визначення у файлі реалізації (.cpp). Виняток із цього - шаблони, де визначення також має міститись у заголовку.
Це та подібні до нього запитання часто задавались у SO - див. Чому у C ++ файли заголовків та файли .cpp? та файли заголовків C ++, наприклад , розділення коду .
Декларації класу та функції, а також документація та визначення вбудованих функцій / методів (хоча деякі воліють розміщувати їх в окремих файлах .inl).
В основному файл заголовка містить скелет класу або декларацію (не змінюється часто)
і cpp-файл містить реалізацію класу (часто змінюється).
Я очікую побачити:
Дійсно відповідь, хоча це те, що не потрібно вносити:
Заголовок Дещо визначає, але нічого не говорить про реалізацію. (Виключаючи шаблони в цьому "метафорі".
З огляду на це, вам потрібно розділити "визначення" на підгрупи, у цьому випадку є два типи визначень.
Зараз я, звичайно, говорю про першу підгрупу.
Заголовок є для визначення плану вашої структури, щоб допомогти решті програмного забезпечення використовувати реалізацію. Можливо, ви захочете розглядати це як "абстракцію" вашої реалізації, про що говорять сміливо, але, я думаю, це цілком підходить у цьому випадку.
Як було сказано в попередніх плакатах, ви декларуєте приватні та загальнодоступні області використання та їх заголовки, це також включає приватні та загальнодоступні змінні. Тепер я не хочу займатися розробкою коду тут, але, можливо, ви захочете врахувати, що ви вкладаєте в свої заголовки, оскільки це Шар між кінцевим користувачем та реалізацією.
Заголовок (.h)
Тіло (.cpp)
Як правило, ви розміщуєте "спільну" частину модуля на .h (частину, яку повинні бачити інші модулі) та "не поділену" частину на .cpp
PD: Так, я включив глобальні змінні. Я використовував їх кілька разів, і важливо не визначати їх у заголовках, інакше ви отримаєте багато модулів, кожен із яких визначає свою змінну.
EDIT: Змінено після коментаря Девіда