52

Вопрос по header, header-files, c++ – Могу ли я написать код C ++ без заголовков (повторяющиеся объявления функций)?

Есть ли способ не писать дважды объявления функций (заголовки) и при этом сохранять одинаковую масштабируемость при компиляции, ясность при отладке и гибкость при разработке при программировании на C ++?

  • Error: User Rate Limit ExceededwasError: User Rate Limit Exceededwill beError: User Rate Limit ExceededmoduleError: User Rate Limit Exceededn2073Error: User Rate Limit ExceededwasError: User Rate Limit Exceededwill be).

    от
  • Error: User Rate Limit Exceeded#includeError: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit ExceededpossibleError: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit ExceededopinionError: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceededdigitalmars.com/d/2.0/dmd-windows.html#interface_filesError: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceededeverything.

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от thewreck
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceededheader-lessError: User Rate Limit Exceededheader-onlyError: User Rate Limit Exceededheader-onlyError: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit ExceededgoodError: User Rate Limit Exceeded

    от
  • Error: User Rate Limit ExceedednecessaryError: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • class a; class b {a * ref;}; class a {b * ref;};

    от
  • @nOrd ... или модули (n2073) будет окончательно принят на языке

    от David Rodríguez - dribeas
  • 3

    Никто еще не упомянул Visual-Assist X под Visual Studio 2012.

    Он имеет множество меню и горячих клавиш, которые вы можете использовать, чтобы облегчить боль при ведении заголовков:

    "Create Declaration" copies the function declaration from the current function into the .hpp file. "Refactor..Change signature" allows you to simultaneously update the .cpp and .h file with one command. Alt-O allows you to instantly flip between .cpp and .h file.

  • 27

    Извините

    но такой вещи, как "наилучшая практика", не существует для устранения заголовков в C ++: это плохая идея, точка. Если вы их так ненавидите, у вас есть три варианта:

    Become intimately familiar with C++ internals and any compilers you're using; you're going to run into different problems than the average C++ developer, and you'll probably need to solve them without a lot of help. Pick a language you can use "right" without getting depressed Get a tool to generate them for you; you'll still have headers, but you save some typing effort

  • 0

    Лучше всего использовать заголовочные файлы

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

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

    в качестве примера рассмотрим указатели, передавая параметры по значению / по ссылке ... и т. д.

    для меня заголовочные файлы позволяют мне правильно структурировать свои проекты

  • 3

    На самом деле ... Вы можете записать всю реализацию в файл. Шаблонные

    классы все определены в заголовочном файле без файла cpp.

    Вы также можете сохранить с любыми расширениями, которые вы хотите. Затем в операторах #include вы включаете свой файл.

    /* mycode.cpp */
    #pragma once
    #include <iostreams.h>
    
    class myclass {
    public:
      myclass();
    
      dothing();
    };
    
    myclass::myclass() { }
    myclass::dothing()
    {
      // code
    }
    

    Тогда в другом файле

    /* myothercode.cpp */
    #pragma once
    #include "mycode.cpp"
    
    int main() {
       myclass A;
       A.dothing();
       return 0;
    }
    

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

  • 8

    Не существует практического способа обойти заголовки. Единственное

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

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

    Кстати, как только вы привыкли, это не так.that плохо больше .. C ++ (и любой другой язык также) имеет больше неприятных вещей.

  • 10

    В своей статье

    Простая поддержка проектирования по контракту в C ++Педро Геррейро заявил:

    Usually, a C++ class comes in two files: the header file and the definition file. Where should we write the assertions: in the header file, because assertions are specification? Or in the definition file, since they are executable? Or in both, running the risk of inconsistency (and duplicating work)? We recommend, instead, that we forsake the traditional style, and do away with the definition file, using only the header file, as if all functions were defined inline, very much like Java and Eiffel do.

    This is such a drastic change from the C++ normality that it risks killing the endeavor at the outset. On the other hand, maintaining two files for each class is so awkward, that sooner or later a C++ development environment will come up that hides that from us, allowing us to concentrate on our classes, without having to worry about where they are stored.

    Это был 2001 год. Я согласился. Сейчас 2009 год, и до сих пор нет «среды разработки, которая скрывает это от нас, что позволяет нам концентрироваться на наших классах». подошел. Вместо этого долгое время компиляции является нормой.

    Note: Ссылка выше кажется мертвой сейчас. Это полная ссылка на публикацию, как она появляется вПубликации раздел сайта автора:

    Педро Геррейро,Simple Support for Design by Contract in C++, TOOLS USA 2001, Proceedings, page 24-34, IEEE, 2001.

  • 55

    использование

    Lzz, Он берет один файл и автоматически создает .h и .cpp для вас со всеми объявлениями / определениями в нужном месте.

    Lzz действительно очень мощный и обрабатывает 99% полного синтаксиса C ++, включая шаблоны, специализации и т. Д. И т. Д. И т. Д.

    Update 150120:

    Более новый синтаксис C ++ '11/14 может использоваться только внутри тел функций Lzz.

  • 37

    Когда я начал писать C

    я чувствовал то же самое, поэтому я тоже изучил это. Ответ в том, что да, это возможно, а нет, вы не хотите.

    Сначала с да.

    В GCC вы можете сделать это:

    // foo.cph
    
    void foo();
    
    #if __INCLUDE_LEVEL__ == 0
    void foo() {
       printf("Hello World!\n");
    }
    #endif
    

    Это дает желаемый эффект: вы объединяете заголовок и источник в один файл, который можно включать и связывать.

    Тогда с нет:

    Это работает только в том случае, если компилятор имеет доступ ко всему источнику. Вы не можете использовать этот трюк при написании библиотеки, которую хотите распространять, но сохраняйте закрытый исходный код. Либо вы распространяете полный файл .cph, либо вам нужно написать отдельный файл .h для вашей библиотеки .lib. Хотя, возможно, вы могли бы автоматически сгенерировать его с помощью препроцессора макроса. Это было бы волосатым, хотя.

    И причина № 2, почему вы этого не хотите, и это, вероятно, самая лучшая причина:compilation speed, Обычно исходные файлы C нужно перекомпилировать только при изменении самого файла или любых файлов, в которые он включен.

    The C file can change frequently, but the change only involves recompiling the one file that changed. Header files define interfaces, so they shouldn't change as often. When they do however, they trigger a recompile of every source file that includes them.

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

  • 5

    There's header file generation software. Я никогда этим не пользовался, но, возможно, стоит разобраться. Например, проверитьmkhdr! Он предположительно сканирует файлы C и C ++ и генерирует соответствующие файлы заголовков.

    (Однако, как указывает Ричард, это, кажется, ограничивает вас от использования определенных функций C ++. См. Ответ Ричарда вместоздесь прямо в этой теме.)

  • 0

    Заpractical 

    целей нет, это невозможно. Технически, да, вы можете. Но, честно говоря, это злоупотребление языком, и вы должны адаптироваться к языку. Или перейти на что-то вроде C #.

  • 0

    Это было "восстановлено" благодаря дубликату ...

    В любом случае, концепция заголовка является достойной, то есть отделяет интерфейс от деталей реализации. Заголовок описывает, как вы используете класс / метод, а не как он это делает.

    Недостатком является деталь внутри заголовков и все необходимые обходные пути. Это основные вопросы, которые я вижу:

    dependency generation. When a header is modified, any source file that includes this header requires recompilation. The issue is of course working out which source files actually use it. When a "clean" build is performed it is often necessary to cache the information in some kind of dependency tree for later.

    include guards. Ok, we all know how to write these but in a perfect system it would not be necessary.

    private details. Within a class, you must put the private details into the header. Yes, the compiler needs to know the "size" of the class, but in a perfect system it would be able to bind this in a later phase. This leads to all kinds of workaround like pImpl and using abstract base classes even when you only have one implementation just because you want to hide a dependency.

    Идеальная система будет работать с

    separate class definition and declaration A clear bind between these two so the compiler would know where a class declaration and its definition are, and would know what the size of a class. You declare using class rather than pre-processor #include. The compiler knows where to find a class. Once you have done "using class" you can use that class name without qualifying it.

    Мне было бы интересно узнать, как это делает D.

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

  • 0

    Вы можете аккуратно разложить свои функции так

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

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

    Лично я считаю, что заголовочные файлы + объявления функций гораздо более желательны для "обдумывания". новый код, но это личное предпочтение, я полагаю ...

  • 0

    Вы можете обойтись без заголовков. Но зачем тратить усилия на то

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

    Когда я писал основные, мне очень нравились номера строк. Но я бы не подумал попытаться вставить их в C ++, потому что это не так, как в C ++. То же самое касается заголовков ... и я уверен, что другие ответы объясняют все причины.

  • 2

    Выcan избегайте заголовков. Полностью. Но я не рекомендую это.

    Вы столкнетесь с некоторыми очень конкретными ограничениями. Одним из них является то, что вы не сможете иметь циклические ссылки (вы не сможете иметь класс Parent, содержащий указатель на экземпляр класса ChildNode, а класс ChildNode также содержит указатель на экземпляр класса Parent. ; d должен быть один или другой.)

    Существуют и другие ограничения, которые просто делают ваш код действительно странным. Придерживайтесь заголовков. Вы научитесь на самом деле любить их (поскольку они дают хороший краткий обзор того, что может сделать класс).

  • 8

    То, что я видел, как некоторые люди, как вы, это

    записыватьeverything in the headers, Это дает желаемое свойство - вам нужно только один раз написать профили метода.

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

  • 0

    Научитесь распознавать

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

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

  • 1

    Разработка полностью возможна без заголовочных файлов. Можно напрямую

    включить исходный файл:

    #include "MyModule.c"
    

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

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

    В настоящее время я занимаюсь разработкой этого метода для одного из моих крупных проектов. Вот список преимуществ, которые я испытал:

    Much less file pollution in your source tree. Faster build times. (Only one object file is produced by the compiler, main.o) Simpler make files. (Only one object file is produced by the compiler, main.o) No need to "make clean". Every build is "clean". Less boiler plate code. Less code = less potential bugs.

    Я обнаружил, что Gish (игра Cryptic Sea, Edmund McMillen) использует вариацию этой техники в своем собственном исходном коде.

  • 1

    Я понимаю твои проблемы. Я бы сказал

    что основной проблемой C ++ является метод компиляции / сборки, который он унаследовал от C. Структура заголовка C / C ++ была разработана во времена, когда кодирование включало меньше определений и больше реализаций. Не бросайте в меня бутылки, но так это выглядит.

    С тех пор ООП покорила мир, и мир больше о определениях, чем реализациях. В результате, включение заголовков делает довольно болезненным работу с языком, в котором базовые коллекции, такие как коллекции в STL, создаются с помощью шаблонов, которые, как известно, трудны для компилятора. Вся эта магия с предварительно скомпилированными заголовками не очень помогает, когда дело доходит до TDD, инструментов рефакторинга, общей среды разработки.

    Конечно, программисты на C не слишком страдают от этого, поскольку у них нет заголовочных файлов, требующих большого количества компиляторов, и поэтому они довольны довольно простой цепочкой инструментов для низкоуровневой компиляции. С C ++ это история страданий: бесконечные предварительные объявления, предварительно скомпилированные заголовки, внешние парсеры, пользовательские препроцессоры и т. Д.

    Однако многие люди не осознают, что C ++ является ЕДИНСТВЕННЫМ языком, который имеет сильные и современные решения для задач высокого и низкого уровня. Легко сказать, что вы должны выбрать другой язык с правильной системой отражения и построения, но это не означает, что мы должны жертвовать решениями для программирования низкого уровня, и нам нужно усложнять вещи с низким уровнем язык смешивается с некоторым решением на основе виртуальной машины / JIT.

    У меня уже давно есть идея, что было бы самым классным на свете иметь «единицу». основанная на c ++ цепочка инструментов, похожая на ту, что и в D. Проблема возникает в кроссплатформенной части: объектные файлы могут хранить любую информацию, никаких проблем с этим, но поскольку в окнах структура объектного файла отличается Что касается ELF, было бы очень трудно реализовать кроссплатформенное решение для хранения и обработки блоков компиляции на полпути.

  • 1

    Насколько я знаю

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

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

  • 2

    Чтобы предложить вариант на популярный ответ rix0rrr:

    // foo.cph
    
    #define INCLUDEMODE
    #include "foo.cph"
    #include "other.cph"
    #undef INCLUDEMODE
    
    void foo()
    #if !defined(INCLUDEMODE)
    {
       printf("Hello World!\n");
    }
    #else
    ;
    #endif
    
    void bar()
    #if !defined(INCLUDEMODE)
    {
        foo();
    }
    #else
    ;
    #endif
    

    Я не рекомендую это, хотя я думаю, что эта конструкция демонстрирует удаление повторения контента за счет повторения наизусть. Я полагаю, это облегчает копирование пасты? Это не совсем добродетель.

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

    Для других читателей: я потратил несколько минут, пытаясь выяснить, включают ли охранники в этом формате, но ничего хорошего не придумал. Комментарии?

  • 2

    Прочитав все остальные ответы

    я обнаружил, что отсутствует пропущенная работа по добавлению поддержки модулей в стандарт C ++. Он не дойдет до C ++ 0x, но предполагается, что он будет рассмотрен в последующем техническом обзоре (а не в ожидании нового стандарта, который займет века).

    Предложение, которое обсуждалосьN2073.

    Плохая часть этого в том, что вы не получите этого, даже с новейшими компиляторами c ++ 0x. Вам придется подождать. В то же время вам придется идти на компромисс между уникальностью определений вheader-only библиотеки и стоимость компиляции.

  • 5

    Вы должны написать функцию

    declaration фактически дважды (один раз в заголовочном файле, один раз в файле реализации). Определение (реализация AKA) функции будет записано один раз в файле реализации.

    Вы можете написать весь код в заголовочных файлах (это на самом деле очень распространенная практика в универсальном программировании на C ++), но это подразумевает, что каждый файл C / CPP, включая этот заголовок, будет подразумевать перекомпиляцию реализации из этих заголовочных файлов.

    Если вы думаете о системе, подобной C # или Java, это невозможно в C ++.