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


многие языки, такие как Java, C#, не отделяют объявление от реализации. C# имеет понятие частичного класса, но реализация и объявление все еще остаются в том же файле.

Почему C++ не имеет той же модели? Это более практично иметь заголовочные файлы?

Я имею в виду текущие и будущие версии стандарта C++.

17 67

17 ответов:

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

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

возможно, если бы intellisense Visual Studio работал лучше для C++, у меня не было бы веской причины ссылаться .H файлы так часто, но даже в VS2008, C++'S intellisense не может коснуться C#' S

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

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

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

даже Бьярн Страуструп назвал заголовочные файлы kludge.

но без стандартного двоичного формата, который включает необходимые метаданные (например, файлы классов Java или файлы .NET PE), я не вижу никакого способа реализовать эту функцию. Лишенный эльф или двоичный файл. out не имеет большой части информации, которую вам нужно будет извлечь. И я не думаю, что информация когда-либо хранится в файлах Windows XCOFF.

на дизайн и эволюция C++, Страуструп выдает еще одну причину...

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

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

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

Это хорошая идея на самом деле. Когда C был создан, C и Unix были своего рода парой. C портировал Unix, Unix запускал C. Таким образом, C и Unix могли быстро распространяться из платформа к платформе, в то время как ОС, основанная на сборке, должна была быть полностью переписана для переноса.

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

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

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


похоже, некоторые люди действительно не верят, что C был написан для порта Unix, поэтому здесь: (С)

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

Томпсон впервые попробовал в 1971 году для использования Фортран на PDP-7, но сдался после первого дня. Затем он написал очень простым языком он называл Б, который он получил на PDP-7. Оно работал, но были проблемы. Во-первых, потому что реализация была интерпретируемый, он всегда будет медленный. Во-вторых, Основные понятия Б, которая была основана на слове-ориентированной Не нуждающийся в представлении, просто не было права на байт-ориентированная машина, как новая PDP-11.

Ричи использовал PDP-11 для добавления типов к B, который некоторое время назывался NB для "нового Б", а затем он начал напишите для него компилятор. "Так что первый этап C был на самом деле эти два этапы в короткой последовательности, первый, некоторые изменения языка от B, действительно, добавление структуры типа без много изменений в синтаксисе; и делать компилятор", - сказал Ричи.

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

Он уже существует и называется D (http://www.digitalmars.com/d/index.html)

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

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

Как C++ разработан прямо сейчас, вам нужно прототипы-так же, как и в C-для статической проверки любого скомпилированного кода, который ссылается на внешние функции и классы. Без файлов заголовков вам придется вводить эти определения классов и объявления функций перед их использованием. Чтобы C++ не использовал заголовочные файлы, вам нужно будет добавить функцию на языке, которая будет поддерживать что-то вроде Java import ключевое слово. Это было бы важным дополнением и изменением; чтобы ответить на ваш вопрос о том, будет ли это практично: я так не думаю-не на все.

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

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

есть значение в определении интерфейса класса в отдельном компоненте к файлу реализации.

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

Модуль 2 имел правильную идею, модули определения и модули реализации. http://www.modula2.org/reference/modules.php

Java/C#'S ответ является неявная же реализация (хотя и объектно-ориентированная.)

заголовочные файлы являются kludge, потому что заголовочные файлы выражают детали реализации (например, частные переменные.)

переходя к Java и C#, я нахожу, что если язык требует поддержки IDE для разработки (например, чтобы интерфейсы публичных классов были доступны для навигации в браузерах классов), то это, возможно, утверждение о том, что код не стоит на своих собственных достоинствах как особенно читаемый.

Я нахожу сочетание интерфейса с детализацией реализации довольно ужасное.

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

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

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

было бы гораздо проще изменить оператор switch на что-то наполовину интеллектуальное. Это нарушило бы только небольшой код. Этого все равно не произойдет.

ОТРЕДАКТИРОВАНО ДЛЯ НОВОЙ ИДЕИ:

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

ни один язык не существует без заголовочных файлов. Это миф.

посмотрите на любой проприетарный дистрибутив библиотеки для Java (у меня нет опыта C#, но я ожидаю, что это то же самое). Они не дают вам полный исходный файл; они просто дают вам файл с пустой реализацией каждого метода ({} или {return null;} или тому подобное) и все, что им может сойти с рук, скрывается скрытым. Вы не можете назвать это ничем, кроме заголовка.

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

файлы заголовков являются неотъемлемой частью языка. Без заголовочных файлов все статические библиотеки, динамические библиотеки, практически любая предварительно скомпилированная библиотека становятся бесполезными. Заголовочные файлы также упрощают документирование всего и позволяют просматривать API библиотеки/файла, не просматривая каждый бит кода.

они также облегчают организацию вашей программы. Да, вы должны постоянно переключаться с источника на заголовок, но они также позволяют вам определить внутренние и частные API внутри реализаций. Например:

MySource.h:

extern int my_library_entry_point(int api_to_use, ...);

MySource.c:

int private_function_that_CANNOT_be_public();

int my_library_entry_point(int api_to_use, ...){
  // [...] Do stuff
}

int private_function_that_CANNOT_be_public() {

}

если вы #include <MySource.h>, тогда вы получите my_library_entry_point.

если вы #include <MySource.c>, то и get private_function_that_CANNOT_be_public.

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

О Да!

после кодирования в Java и C# это действительно раздражает иметь 2 файла для каждого класса. Поэтому я думал, как я могу объединить их, не нарушая существующий код.

на самом деле, это очень легко. Просто поместите определение (реализацию) в раздел #ifdef и добавьте определение в командной строке компилятора для компиляции этого файла. Вот и все.

вот пример:

/* File ClassA.cpp */

#ifndef _ClassA_
#define _ClassA_

#include "ClassB.cpp"
#include "InterfaceC.cpp"

class ClassA : public InterfaceC
{
public:
    ClassA(void);
    virtual ~ClassA(void);

    virtual void methodC();

private:
    ClassB b;
};

#endif

#ifdef compiling_ClassA

ClassA::ClassA(void)
{
}

ClassA::~ClassA(void)
{
}

void ClassA::methodC()
{
}

#endif

в командной строке скомпилируйте этот файл с

-D compiling_ClassA

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

#include "ClassA.cpp"

конечно, добавление define в командной строке может быть легко добавлено с расширением макроса (компилятор Visual Studio) или с автоматическими переменными (gnu make) и с использованием той же номенклатуры для имени define.

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