Пространства имен C ++, сравнение с пакетами Java - PullRequest
39 голосов
/ 21 января 2010

Недавно я сделал несколько Java-кодов и привык к очень специфическим системам именования пакетов, например, с глубоким вложением. com.company.project.db. Это прекрасно работает в Java, AS3 / Flex и C #. Я видел ту же самую парадигму, примененную и в C ++, но я также слышал, что плохо рассматривать пространства имен C ++ как прямые аналоги пакетов Java.

Это правда и почему? Чем пространства имен / пакеты похожи и чем отличаются? Какие проблемы могут возникнуть, если вы используете глубоко вложенные пространства имен?

Ответы [ 7 ]

26 голосов
/ 21 января 2010

В C ++ пространства имен - это просто разделение доступных имен. Пакеты Java о модулях. Иерархия именования является лишь одним из аспектов.

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

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

namespace a{ namespace b{ int x; } }
namespace b{ string x; }
namespace a
{
  b::x = 42;
}

Это законно? Это очевидно, что происходит? Вам нужно знать приоритет разрешения пространства имен, чтобы ответить на эти вопросы.

19 голосов
/ 21 января 2010

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.

4 голосов
/ 06 января 2017

Вот несколько причин, почему и как пространства имен C ++ отличаются от пространств имен Java или C #.

Предотвращение конфликтов

В языках Java / C # пространства имен предназначены для избежания конфликтов между именами в разных частях библиотек классов. У вас может быть класс «Watcher» в 5 разных местах иерархии пространства имен в C #. В C ++, если у вас есть классы с одинаковыми именами, встречающиеся в вашей библиотеке, вы помещаете внутрь другого класса вместо создания пространств имен. Такие вложенные классы хороши и приветствуются, и фактически синтаксис также обрабатывает, как будто класс является пространством имен, используя :: operator.

Сколько должно быть вложенных пространств имен?

Популярные библиотеки, такие как Boost, Eigen и, конечно, STL, дают хорошие примеры. в этих библиотеках, как правило, содержится почти все содержимое одного пространства имен, например std:: или boost:: или eigen::. Немногие компоненты получают свое собственное пространство имен, например std::ios или boost:filesystem. Не существует согласованных правил относительно того, когда использовать второй уровень, но кажется, что большие или отдельно разработанные / поддерживаемые или дополнительные компоненты обычно получают свои собственные пространства имен. Третий уровень еще более редок. Обычно я использую структуру company::project и для большой независимо используемой подсистемы проектов company::project::component.

Предотвращение конфликтов во внешних библиотеках

Теперь главный вопрос: что если вы получите две библиотеки от двух разных людей, которые имеют одинаковые пространства имен и классы? Такая ситуация довольно редкая, потому что большинство людей, как правило, оборачивают свои библиотеки, по крайней мере, в название своего проекта Даже если имена проектов совпадают, еще реже вы используете библиотеки из обоих. Однако иногда плохие решения принимаются для имен проектов (ааа ... "metro", "apollo" ...) или даже пространства имен просто не используются вообще. Если это произойдет, вы включите #include для одной или обеих библиотек в пространство имен, и конфликт разрешится! Это одна из причин, почему люди не слишком беспокоятся о конфликтах, потому что их разрешение тривиально. Если вы будете следовать практике использования company::project, то конфликты станут очень редкими.

Различия в языках

Хотя C ++ предоставляет оператор using namespace точно так же, как C #, обычно считается плохой практикой «импортировать» все в собственное пространство имен. Причиной этого является то, что заголовок может содержать много «плохих» вещей, включая переопределение вещей, которые могут вас полностью удивить. Это совсем не похоже на C # / Java, где вы получаете чистый открытый интерфейс только тогда, когда вы делаете эквивалент using namespace. (примечание: в C ++ вы можете достичь того же самого, используя шаблон Pimpl, но обычно это слишком много лишних операций и фактически не все библиотеки делают это). Таким образом, вы почти никогда не хотите делать using namespace. Вместо этого вы делаете typedefs (или using name =) для того, что вы действительно хотите использовать. Это снова делает нецелесообразным использование глубоко вложенных пространств имен.

Организационный код

В Java / C # люди часто упорядочивают код в папках. Обычно, когда папка содержит более 20 или даже 10 файлов, люди начинают задумываться о папках. В C ++ все более разнообразно , но для многих крупных проектов предпочтительнее более плоские структуры каталогов. Например, папка std стандартной библиотеки содержит 53 файла, а проект Facebook folly , похоже, идет по тому же пути. Я думаю, что одной из причин этого, вероятно, является тот факт, что люди Java / C # чаще используют визуальные IDE и используют прокрутку мыши в навигации по папкам, а не в консоли, где вы можете использовать подстановочные знаки для поиска файла в плоской структуре. Кроме того, программисты на C ++ абсолютно не уклоняются от помещения нескольких классов в один файл и именования файла как логической единицы, а не имени класса в отличие от C # или Java. Это ускоряет компиляцию, что очень важно для больших проектов. Несмотря на то, что для уровня языка нет необходимости иметь собственное пространство имен для каждой папки, многие разработчики C ++ предпочитают назначать свое собственное пространство имен для каждой папки и поддерживать иерархию папок на уровне 2 или менее.

Возможное исключение

В C ++ вы можете ссылаться на A::B::C::D как просто C::D, если вы уже находитесь внутри A::B. Поэтому, если у вас есть закрытый код или менее используемые классы, которые вы хотите продвинуть дальше вниз, вы можете сделать это, сохраняя при этом собственную относительную глубину до 2 или около того. В этом случае вы также можете создать папку для каждого уровня, чтобы расположение файлов было предсказуемым. В целом, в этой области нет золотых стандартов, но вы не хотите зацикливаться на глубоко вложенных пространствах имен, имитирующих C # / Java.

Относящиеся

0 голосов
/ 22 января 2011

Я думаю, что в предыдущих ответах чего-то не хватает, и это одна из причин, почему мне действительно нравится C ++.

Представьте, что вы программируете графическое приложение, и вдруг вы понимаете, что есть что-то общее между всеми вашими виджетами. Вы хотите, чтобы у всех них была новая функция. Что ты делаешь?

1) Редактировать базовый класс виджетов? ОК, но, скорее всего, у вас нет доступа к нему. Возможно, существует проблема с лицензированием, которая не позволяет вам вносить свои изменения. Даже если вы можете просто сделать это, если это то, что имеет смысл только для вашего проекта, авторы не включат его в свой будущий выпуск, и обновление инструментария будет более болезненным

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

Замечательная особенность пространств имен C ++ в том, что у вас есть дополнительный способ инкапсулировать stuff в уже существующую систему. Мало того, что вы можете инкапсулировать в уже существующие библиотеки, которые вы не можете редактировать, вы можете инкапсулировать аналогичные концепции, которые вы не можете легко вставить в вашу иерархию классов / объектов.

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

0 голосов
/ 21 января 2010

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

  1. На производительность C ++ (как-то) влияют длинные полные имена методов, например, namespace1::namespace2::namespace3::classX::method123()

  2. Вы можете установить ограничение на допустимую длину символа в компиляторе / компоновщике

0 голосов
/ 21 января 2010

В C ++ основной единицей проектирования и реализации является класс, а не пространство имен. Пространства имен были предназначены для предотвращения столкновений имен в больших библиотеках, а не для выражения концепций.

Классы имеют несколько преимуществ перед пространствами имен:

  • они могут иметь конструкторы и деструкторы
  • они могут иметь частных членов
  • их нельзя открыть заново

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

0 голосов
/ 21 января 2010

Вы можете иметь вложенные пространства имен в C ++.

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

...