Пространства имен С++, сравнение с пакетами Java
Недавно я сделал кучу Java-кодирования и привык к очень конкретным системам именования пакетов с глубоким вложением, например. com.company.project.db
. Это отлично работает в Java, AS3/Flex и С#. Я также видел ту же парадигму, что и на С++, но я также слышал, что это плохо, чтобы рассматривать пространства имен С++ как прямые копии пакетов Java.
Это правда и почему? Как пространства имен и пакеты одинаковы и разные? Какие проблемы могут возникнуть, если вы используете глубокие вложенные пространства имен?
Ответы
Ответ 1
В пространствах имен С++ речь идет только о разделении доступных имен. Пакеты Java - это модули. Иерархия именования - всего лишь один из аспектов.
В С++ нет ничего плохого, с глубоко вложенными пространствами имен, за исключением того, что они обычно не нужны, поскольку за ними нет системной системы, а дополнительные слои просто добавляют шум.
Обычно этого достаточно, чтобы иметь один или два уровня пространства имен, с нечетным дополнительным уровнем для внутренних деталей (часто называемых только деталями).
Существуют также дополнительные правила для пространств имен С++, которые могут вас поймать, если они используются, например аргумент-зависимый поиск, а также правила вокруг разрешение на родительские уровни. WRT, возьмите:
namespace a{ namespace b{ int x; } }
namespace b{ string x; }
namespace a
{
b::x = 42;
}
Является ли это законным? Является ли это очевидным, что происходит?
Вам необходимо знать точность разрешения пространства имен для ответа на эти вопросы.
Ответ 2
Пакеты Java не вложены, они плоские. Любая видимая вложенность - не более чем соглашение об именах.
Например, пакет com.company.project.db
не имеет отношения к com.company.project
или com.company.project.db.x
. Код в com.company.project.db
больше не имеет доступа к коду в com.company.project.db.x
, чем код в a.b.c
.
Ответ 3
У вас могут быть вложенные пространства имен в С++.
Они не работают так же, как в java, хотя, очевидно. Пакеты Java на самом деле намного лучше определены, и нет реальной статической инициализации. С пространствами имен С++ полезны, но все еще имеют справедливую часть опасности.
Ответ 4
В С++ основной единицей проектирования и реализации является класс, а не пространство имен. Пространства имен были предназначены для предотвращения конфликтов имен в больших библиотеках, а не для выражения понятий.
Классы имеют несколько преимуществ по сравнению с пространствами имен:
- у них могут быть конструкторы и деструкторы
- у них могут быть закрытые члены
- они не могут быть повторно открыты.
Однако, я бы дважды посмотрел на любые глубоко вложенные отношения. Это не очень хороший способ разработки программного обеспечения и приводит к нечитаемому коду, независимо от того, используете ли вы классы или пространства имен.
Ответ 5
Я брошу пару вещей, которые я слышал, но не знаю правдивости, пожалуйста, помогите подтвердить/развеять их.
-
Производительность С++ (каким-то образом) затрагивается длинными полностью указанными именами методов, например, namespace1::namespace2::namespace3::classX::method123()
-
Вы можете ограничить допустимую длину символа в компиляторе/компоновщике
Ответ 6
Я думаю, что в предыдущих ответах что-то не хватает, и это одна из причин, по которой мне очень нравится С++.
Представьте, что вы программируете графическое приложение, и вдруг вы осознаете, что среди всех ваших виджетов есть что-то общее. Вы хотите, чтобы все они имели новую функцию. Что вы делаете?
1) Отредактируйте базовый класс виджетов? Хорошо, но, скорее всего, у вас нет доступа к нему. Возможно, есть проблема с лицензированием, которая мешает вам выполнять свою собственную модификацию. Даже если вы можете просто сделать это, если это то, что имеет смысл только для вашего проекта, авторы не будут включать его в свой будущий выпуск, а обновление инструментария будет более болезненным.
2) Создайте класс интерфейса/мультинаследование? В зависимости от вашего существующего кода будет больш-больно обновлять каждый класс, связанный с виджетами. Сделайте это, и ваш код будет стоить дороже, потому что все, определяющие новый класс, должны знать, что они предположительно наследуют от вашего интерфейса. Стать зависимым от дисциплины других людей очень рискованно.
Замечательная вещь в пространствах имен С++ заключается в том, что у вас есть дополнительный способ инкапсулировать материал в уже существующую систему. Не только вы можете инкапсулировать в уже существующие библиотеки, которые вы не можете редактировать, вы можете инкапсулировать аналогичные концепции, которые вы не можете легко вставить в свою иерархию классов/объектов.
Java заставляет вас больше ориентироваться на чистый дизайн ООП. Уверен, что я говорю вам, что вы грязный хак и не изящный, но есть много ленивых программистов, которые не тратят время на разработку своих проектов.
Ответ 7
Вот несколько причин, почему и как пространства имен С++ отличаются от пространства имен Java или С#.
Предотвращение конфликтов
В языках Java/С# пространства имен предназначены для предотвращения конфликтов между именами в разных частях библиотек классов. У вас может быть класс под названием "Наблюдатель" в 5 разных местах иерархии пространства имен в С#. В С++, если у вас одинаковые именованные классы, встречающиеся в вашей библиотеке, вы помещаете внутри другого класса вместо создания пространств имен. Такие вложенные классы прекрасно и поощряются, и на самом деле синтаксис также рассматривает, как если бы класс был пространством имен с использованием:: operator.
Сколько вложенных пространств имен должно быть?
Популярные библиотеки, такие как Boost, Eigen и, конечно же, STL - хорошие примеры. эти библиотеки, как правило, содержат почти все содержимое одного пространства имен, например std::
или boost::
или eigen::
. Немногие компоненты получают свое собственное пространство имен, например std::ios
или boost:filesystem
. Не существует согласованных правил, когда использовать второй уровень, но он кажется большим или раздельно разработанным/поддерживаемым, или необязательные компоненты обычно получают свои собственные пространства имен. Третий уровень еще реже. Обычно я использую структуру company::project
и для большой независимо используемой подсистемы проектов company::project::component
.
Предотвращение конфликтов во внешних библиотеках
Теперь большой вопрос: что, если вы получаете две библиотеки от двух разных людей, которые имеют одинаковые пространства имен и классы? Эта ситуация довольно редка, потому что большинство людей склонны обертывать свои библиотеки по крайней мере в имени своего проекта. Даже если имена проектов одинаковые, еще реже, что вы в конечном итоге используете библиотеки из обоих. Однако иногда принимаются неправильные решения для названий проектов (ahm... "metro", "apollo"...) или даже пространства имен просто не используются вообще. Если это произойдет, вы переносите #include для обеих или обеих библиотек в пространство имен, и конфликт разрешен! Это одна из причин, почему люди не слишком беспокоятся о конфликтах, потому что их решение тривиально. Если вы следуете практике использования company::project
, тогда конфликты становятся очень редкими.
Различия в языках
Хотя С++ предоставляет инструкцию using namespace
точно так же, как и С#, ее обычно считают плохой практикой "импортировать" все в ваше собственное пространство имен. Причина этого в том, что заголовок может содержать много "плохих" вещей, включая переопределение вещей, которые могут вас полностью удивить. Это совсем не похоже на С#/Java, где вы получаете чистый публичный интерфейс, когда вы выполняете эквивалент using namespace
. (обратите внимание: на С++ вы можете добиться того же, используя шаблон Pimpl, но обычно это слишком большая часть дополнительной сантехники, и на самом деле это делают немногие библиотеки). Поэтому вы почти никогда не хотите делать using namespace
. Вместо этого вы делаете typedefs
(или using name =
) за то, что вы действительно хотите использовать. Это снова делает невозможным использование глубоко вложенных пространств имен.
Организация кода
В Java/С# люди часто организуют код в папках. Обычно, когда папка насчитывает более 20 или даже 10 файлов, люди начинают думать о папках. В С++ вещи более разнообразны, но для многих крупных проектов предпочтительны более плоские структуры каталогов. Например, стандартная библиотека std folder имеет 53 файла и Facebook folly, похоже, идет по тому же маршруту. Я думаю, что одной из причин этого, вероятно, является тот факт, что люди Java/С# используют визуальные IDE больше и используют прокрутки мыши в навигации по папкам, а не консоль, где вы можете использовать wild cards для поиска файла в плоской структуре. Кроме того, программисты на C++ абсолютно не уклоняются от размещения нескольких классов в одном файле и имени файла как логической единицы, а не как имя класса, в отличие от С# или Java. Это ускоряет сборку, что очень важно для крупных проектов. Несмотря на отсутствие требований к языковому уровню для наличия собственного пространства имен для каждой папки, многие разработчики С++ предпочитают назначать свое собственное пространство имен для каждой папки и поддерживать иерархию папок на уровне 2 или менее.
Возможное исключение
В С++ вы можете ссылаться на A::B::C::D
как на C::D
, если вы уже находитесь внутри A::B
. Поэтому, если у вас есть закрытый код или менее используемые классы, которые вы хотите продвигать дальше, вы можете сделать это, сохраняя при этом свою собственную относительную глубину до 2 или около того. В этом случае вам также может понадобиться создать папку для каждого уровня, чтобы расположение файлов было предсказуемым. В общем, в этой области нет золотых стандартов, но вы не хотите переходить за борт с глубоко вложенными пространствами имен, имитирующими С#/Java.
Похожие