Что делать, если я ненавижу заголовочные файлы C ++?

25

Меня всегда смущали заголовочные файлы. Они такие странные: вы включаете файл .h, который не включает .cpp, но .cpp тоже как-то компилируется.

Недавно я присоединился к командному проекту, и, конечно, используются как .h, так и .cpp.
Я понимаю, что это очень важно, но я не могу жить с копированием каждого объявления функции в каждом из нескольких классов, которые у нас есть.

Как эффективно обрабатывать соглашение из двух файлов?
Существуют ли какие-либо инструменты, чтобы помочь с этим, или автоматически изменить один файл, который выглядит как пример ниже, на .h и .cpp? (специально для MS VC ++ 2010)

class A
{
...
    Type f(Type a,Type b)
    {
        //implementation here, not in another file!
    }
...
};

Type f(Type a)
{
     //implementation here
}
...
Олег Припин
источник
8
Этот вопрос может пойти двумя путями: «Зачем нам нужны заголовки при использовании c ++» или «Как вы думаете, должен ли современный язык, предназначенный для компиляции, использовать заголовки?» Как есть, в названии написано «Что я делаю» и «Ненавижу», что вызывает множество флагов.
Тим Пост
4
Ваш вопрос создает впечатление, что вы не понимаете C ++ или то, как система, которую вы используете, компилирует ее. Научитесь правильно его использовать, а затем задавайте более субъективные вопросы.
Дэвид Торнли
31
Ваше первое предложение означает, что вы «не все понимаете в заголовках». Включение файла .h не приводит к тому, что соответствующий файл .cpp также «каким-то образом компилируется». Вы сами компилируете .cpp файлы самостоятельно. Если вы не скомпилировали соответствующий .cpp, то включение заголовка без соответствующего объектного файла приведет к сбою компоновщика.
Пол Бучер
5
Что делать? Найдите другой язык, если он вас так сильно беспокоит.
Пол Натан
5
О «не может жить с копированием»: всякий раз, когда кто-то обновляет функцию, он должен обновлять все места, где она вызывается. Так как вызывающих абонентов найти намного сложнее, чем объявление в заголовочном файле, обновление заголовка является лишь незначительной деталью.
Sjoerd

Ответы:

15

Пишем больше рефакторинга на C ++

В C ++ вам не нужно использовать заголовки вообще. Вы можете определить весь объект в одном файле так же, как в C # или Java. Разработчики C обычно хранят только внешние вызовы в заголовочном файле. Все внутренние вызовы будут определены в файле .c. Точно так же вы можете зарезервировать ваши файлы C ++ .h для классов / интерфейсов (чисто виртуальных абстрактных классов) / и т.д. которые предназначены для совместного использования за пределами DLL. Для внутренних классов / структур / интерфейсов и т. Д. Вы просто включите нужный файл .cpp:

#include<myclass.cpp>

Кажется, это не самый популярный подход, но это законный C ++. Это определенно будет возможность для всего вашего внутреннего кода. Это позволяет внутреннему коду и набору классов меняться более радикально, обеспечивая при этом более стабильный интерфейс для взаимодействия кода вне вашей библиотеки / исполняемого файла.

Наличие всего вашего класса в одном файле облегчит выполнение того, что вы хотите. Это не решит проблему переименования метода и необходимости поиска в каждом месте, где вызывается метод, но это обеспечит более понятные сообщения об ошибках. Нет ничего хуже, чем когда ваш заголовок объявляет метод одним способом, но вы реализуете его по-другому. Другой код, который вызывает файл заголовка, будет правильно скомпилирован, и вы получите исключение для ссылки, а файл реализации будет тем, который жалуется на то, что метод не был определен. Когда вы определяете каждый метод на месте (в фактическом объявлении класса), вы получите одно и то же сообщение об ошибке, независимо от того, какой файл содержит его.

Вы также можете посмотреть на этот вопрос: Хорошие инструменты рефакторинга для C ++

Как C / C ++ разрешает файлы заголовка / реализации

На базовом уровне C (а C ++ построен на этой основе), файлы заголовков объявляют обещание функции / struct / variable, которой достаточно, чтобы компилятор мог создать объектный файл. Аналогично, заголовочные файлы C ++ объявляют обещание функций, структур, классов и т. Д. Именно это определение использует компилятор для резервирования пространства в стеке и т. Д.

Файлы .c или .cpp имеют реализацию. Поскольку компилятор преобразует каждый файл реализации в объектный файл, возникают нереализованные понятия (то, что было объявлено в заголовке). Компоновщик связывает хуки с реализациями в других объектных файлах и создает больший двоичный файл, который включает весь код (разделяемая библиотека или исполняемый файл).

VS Specific

Что касается работы с таковыми в Visual Studio, есть несколько мастеров, которые помогают сделать вещи немного проще. Мастер нового класса создаст вашу подходящую пару заголовочных файлов и файлов реализации. Есть даже функция браузера классов, которая позволит вам объявлять новые методы. Он вставит определение в заголовок и заглушку реализации в файл .cpp. Visual Studio имеет эти функции более десяти лет (пока я их использовал).

Берин Лорич
источник
Проблема в том, что я все время сильно изменяю классы, а не просто добавляю новые функции и т. Д.
Олег Припин
5
@BlaXpirit: Итак, почему вы все время сильно меняете классы? Одной из идей OO design является создание множества достаточно стабильных строительных блоков. Если бы я сильно модифицировал классы, я бы хотел более динамичный язык, такой как Common Lisp или Python.
Дэвид Торнли
2
Это то, что я делаю. Я улучшаю / модифицирую «строительные блоки» и добавляю новые
Олег Припин
C ++ никогда не был дружественным к рефакторингу. Концепция рефакторинга не набирала обороты, пока не появились инструменты, которые сделали ее действительно простой в Java IDE. ПРИМЕЧАНИЕ: эти функции были доступны для разработчиков Smalltalk и других языков, но они не стали мейнстримом, пока не стали доступны для многих людей. До сих пор я еще не видел, чтобы кто-то разумно реализовал это для C ++. Возможно, Решарпер из JetBrains? Я знаю, что это делает C # и VB код, но я не уверен, что это даст вам рефакторинг C ++.
Берин Лорич
@Berin: я искал инструменты рефакторинга C ++ год или два назад и обнаружил две вещи. В то время они были довольно дорогими, и я не видел пробные версии, поэтому я не знаю, что они сделали. Более того, один работал только с emacs, что ограничивало бы его эффективность в магазине Visual Studio.
Дэвид Торнли
13

Станьте разработчиком Java.

Если вам действительно нужно продолжать разработку на C ++, вы можете попробовать использовать IDE. Часто они предлагают какой-то механизм, с помощью которого вы можете добавить метод к классу, и он автоматически помещает объявление в файл .h и определение в файл .cpp.

Пол Бучер
источник
2
kthx, я немного знаю Java, но вы не можете создавать низкоуровневые библиотеки Win32 DLL, не так ли?
Олег Припин
41
Я не знаю почему, но «Стать разработчиком Java» звучит как оскорбление: D.
Оливер Вейлер
2
Если вы хотите сделать низкий уровень, забудьте все о «легких языках». Низкий уровень стоит пота и слез.
Batibix
5
Не особенно полезный ответ.
ChrisF
1
@OliverWeiler Я не воспринимаю «стать разработчиком Java» как оскорбление. Я программирую как на C ++, так и на Java, но на данный момент я предпочитаю Java, потому что гораздо проще сидеть и громко писать код, который работает (и более переносим). Если вы по какой-то причине не любите существование заголовочных файлов, пробный Java может быть правильным выбором (хотя странно, что вы ненавидите заголовочные файлы; я бы подумал об изменении в IDE).
Трикси Вольф
7

Вас может заинтересовать программа makeheaders от Hwaci (тех, кто делает SQLite и Fossil).

Также взгляните на то, как строится ископаемое, чтобы иметь представление.

Benoit
источник
5
Спрашивающий по-прежнему должен хорошо понимать отношения между .h и .cpp.
Работа
2
Я понимаю основы. Ответ, кажется, как раз то, что мне нужно.
Олег Припин
4

Когда вы пишете первые строки нового класса, это обычно происходит потому, что вам нужно это в одном месте только в это время. Позже он может быть использован в большем количестве мест, но изначально это обычно не так.

Многие из моих классов начинаются в верхней части текущего файла .cpp. Когда он достаточно стабилизируется, чтобы использовать его в нескольких местах, я вставляю его в заголовок. Хотя часто класс исчезает так же быстро, как и появился.

Сьерд
источник
-1

В качестве предложения по обработке заголовочных файлов C ++ обычно их используют без расширения файла или суффикса файла, как это делают библиотеки "GCC".

Если это ваш случай, я предлагаю использовать расширение .hpp (или unleast " .hxx") или суффикс файла.

Возможно, вам придется настроить ваш компилятор, среду разработчика или встроенную программу.

umlcat
источник
3
Вы говорите о том, как, когда вы включаете файл, как #include <iostream>? Это не только для библиотеки GCC. Фактически, это определено в стандарте C ++ 1997 года , раздел 17.3.1.2. Я бы не стал называть такие файлы. Вы можете, но причина, по которой стандартная библиотека C ++ сделала это, вероятно, состояла в том, чтобы избежать конфликтов имен. Я действительно нахожу это очень странным, когда компиляторы автоматически добавляют «.h», когда вы включаете заголовок, мне это кажется довольно нестандартным. И я никогда не вижу ни одного заголовка имени без суффикса, за исключением стандартной библиотеки c ++.
vedosity
1
Кроме того, я должен отметить, что все компиляторы, которые я использовал, за исключением borland (который я очень ненавижу), не добавляют автоматически «.h» или «.hpp» или «.hxx» при попытке включить файл без суффикса. Не ожидайте, #include <someclass>что вас будут читать как #include <someclass.hpp>на всех компиляторах. Ваш код сломается.
vedosity