И класс и пространство имен?
Этот вопрос о шаблоне, который я вижу, я использую все больше и больше: наличие класса и пространства имен для связанных понятий. Я думаю, что это обусловлено главным образом артефактами языка C ++, но не полностью.
Я полагаю, что вопрос верхнего уровня: это хорошая идея? Иметь класс и пространство имен для связанных понятий?
Вопрос нижнего уровня:
Каков наилучший способ сделать это?
Класс вложен в пространство имен ?:
namespace Foo_Namespace {
class Foo_Class {
...
};
}
Или отдельный, одноранговый, класс и пространство имен?
class Foo_Class {
...
};
namespace Foo_Namespace {
// non-class free functions, etc.
}
Я должен признать, что склоняюсь к тому, чтобы вкладывать класс в пространство имен.
Хотя это приводит к безобразным именам.
Но даже если я сделаю это, какие соглашения об именах я должен использовать:
Следующее является слишком длинным, приводя к действительно уродливым именам Foo_Namespace :: Foo_Class
namespace Foo_Namespace {
class Foo_Class {
...
};
}
Не обязательно использовать какие-либо суффиксы или индикаторы в названии:
namespace Foo {
class Foo {
...
};
}
Но тогда я не уверен, когда смотрю на Foo :: bar (), является ли это свободной функциональной панелью в пространстве имен :: Foo, т.е. :: Foo :: bar (), или функцией-членом в классе Foo в пространстве имен :: Foo :: Foo :: бар ().
И такие имена, как :: Foo :: Foo :: bar, все еще не очень хорошие.
В настоящее время я делаю
Не обязательно использовать какие-либо суффиксы или индикаторы в названии:
namespace Foo_ns {
class Foo {
...
};
}
главным образом потому, что я обычно сначала создаю класс, а потом понимаю, что пространство имен было бы неплохо.
Интересно, следует ли мне восстановить соглашение об именах, которое я не использовал годами: _c для класса, _ns для пространств имен:
namespace Foo_ns {
class Foo_c {
...
};
}
Подробности:
Я не буду повторять то, что я сказал выше, но я добавлю немного больше.
Самая практическая причина, по которой я знаю использование пространства имен в дополнение к классу, заключается в том, что вам разрешено делать предварительные объявления свободных функций в пространстве имен, но вам не разрешено делать предварительные объявления некоторых методов класса. То есть с классом вы должны объявить все это или ничего. В то время как со свободными функциями в целом и свободными функциями в пространствах имен в частности, вы можете объявить это по частям. Части могут быть объявлены в разных заголовочных файлах. Вместо #inclusive массивной библиотеки только для заголовков для массивного класса, вы можете использовать предварительные объявления только для одной или двух функций. И т.п.
(См., Например, http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Forward_Declarations, хотя Google netly идет против предварительных деклараций, а не с заголовком.)
Другая причина в том, что пространство имен позволяет самому классу оставаться маленьким.
Самым большим преимуществом для класса является то, что класс может быть передан в шаблон, тогда как пространство имен не может быть.
Я предпочитаю, чтобы класс был вложен в пространство имен Foo_ns / Foo_ns :: Foo_c, а не в качестве пиров Foo_ns / Foo_c, потому что часто классу нужны вспомогательные классы, например Foo_ns / Foo_ns :: Foo_c / Foo_ns :: Foo_Helper_c. Если класс и пространство имен являются одноранговыми, кажется странным иметь Foo_ns / Foo_c, но Foo_ns :: Foo_Helper_c.
Мне нравятся пространства имен, потому что я согласен с Андреем Александрешиу: http://laser.inf.ethz.ch/2012/slides/Alexandrescu/1-C++%20course%20parts%201%20and%202.pdf
Class methods vs. free functions
• Conventional wisdom: methods are cool, free functions are so 1960s
• Yet:
◦ Free functions improve encapsulation over methods
◦ Free functions may be more general
◦ Free functions decouple better
◦ Free functions support conversions on their left-hand argument
Surprising fact #2
Making a function a method should be
your last, not first, choice
(c) 2012– Andrei Alexandrescu. 32 / 59
Лучше создавать бесплатные функции, чем методы в классах.
Но иногда нужно просто использовать классы — например, для шаблонов.
Соглашение об именах
Я использовал Foo_ns / Foo_ns :: Foo_c в прошлом
Я использую Foo_ns / Foo_ns :: Foo сейчас
(Кстати, я склонен использовать Class_Names_With_Underscores_and_Initial_Caps, а не CamelCase.)
Если это имеет смысл, я могу исключить суффикс _ns в пространстве имен — например, где пространство имен и класс (ы) не обязательно должны иметь одно и то же имя.
Мне не нравится делать их с одинаковыми именами. Рассмотрим конструктор для класса Foo внутри пространства имен foo:
:: Foo :: Foo :: Foo ()
против
:: Foo_ns :: Foo :: Foo ()
Последнее не намного лучше, но немного менее запутанно.
Я думаю, что обычно я создаю класс самостоятельно, не вкладывая его в пространство имен.
Фактически, я, вероятно, добавлю несколько статических методов, прежде чем я пойму, что класс, вложенный в пространство имен, будет лучше. На этом этапе может возникнуть проблема с рефакторингом, и я иногда заканчиваю тем, что создаю функции пересылки, которые перенаправляют от статического метода класса к свободной функции в пространстве имен, или наоборот. Это заставляет меня сожалеть о том, что бот перешел в класс с пространством имен прямо с шага 1.
Заключение
Мой текущий BKM — Foo_ns / Foo_ns :: Foo, т.е.
namespace Foo_ns {
class Foo {
...
};
}
Буду признателен за любые предложения, любые улучшения.
Или я просто сломлен за это?
Я рекомендую разместить ваш класс в пространстве имен со связанными функциями. Большая причина для этого зависимый от аргумента поиск (ADL). Когда вы вызываете функцию, не являющуюся членом, которая имеет аргумент типа класса, имя функции ищется во вложенном пространстве имен этого класса. Так что, если у вас есть, скажите:
namespace foo {
class bar { };
void baz(bar);
}
Если вы когда-нибудь захотите позвонить baz
вам не нужно явно указывать пространство имен, в котором оно содержится, вы можете просто сделать:
foo::bar x;
baz(x);
Без квалификации baz
компилятор все еще нашел функцию, потому что она находится в пространстве имен, которое включает тип ее аргумента. Таким образом, C ++ считает, что содержимое окружающего пространства имен класса является частью интерфейса этого класса. Реализация функций, которые являются частью интерфейса класса, как не являющихся членами, не дружественных функций, таким образом, позволяя ADL найти функцию, увеличивает инкапсуляцию. Если функция не необходимость доступ к внутренним объектам класса, не делайте его членом этого класса — вместо этого поместите его в то же пространство имен, что и этот класс.
Однако подозрительно, что ваше пространство имен имеет то же имя, что и ваш класс. Обычно в пространстве имен будет более одного класса, и даже если их не будет, имена будут другими. Имя пространства имен должно описывать его содержимое, а не только отдельный класс внутри него.
отдельный Foo_Class
а также Foo_Namespace
это определенно неправильно, это помешает вам использовать Argument Dependent Lookup для поиска функций в пространстве имен, которые предназначены для использования с классом.
Таким образом, класс должен быть вложен в пространство имен, если в пространстве имен будут функции, принимающие аргументы типа класса.
Использование одного и того же имени для класса и пространства имен немного сбивает с толку, в некоторых случаях может привести к неоднозначности.
Также необычно называть пространство имен начальной заглавной буквой. Если имена ваших классов начинаются с заглавной буквы, вы можете использовать пространство имен foo
и класс Foo
, давая foo::Foo
,
Не будет ли пространство имен содержать более одного класса? Это звучит необычно для меня. Я бы назвал пространство имен после все его содержимое, просто не одного типа. Если это socket
класс я бы положил в networking
пространство имен, например.
Я думаю _c
Суффиксы на занятиях совершенно нелепы. Мне не нравится _ns
суффикс на пространствах имен также, единственная спасительная благодать будет Foo_ns::Foo
лучше, чем Foo::Foo
но я бы все еще сделал это foo::Foo
вместо.
(Остальная часть вашего вопроса выглядит так: «Почему пространства имен хороши?», Которые на самом деле не нужно объяснять, они были добавлены в язык по какой-то причине, и хороший совет использовать не-члены для частей интерфейса, которые не не нужно быть участниками. Google правы, в общем случае используют заголовки, а не пересылки объявлений, это не меняет того факта, что вы можете повторно открывать пространства имен в отдельных заголовках, чтобы добавлять новые имена в пространство имен, что является вашим преимуществом » переписываю.)
Мне не нравятся повторяющиеся имена «фракталов», и я стараюсь избегать этого. Для вашего случая я бы попробовал что-то вроде пространства имен «Foo» и вложенного класса «Type». Может быть, какое-то другое имя будет более подходящим, но это зависит от вашего фактического варианта использования. Вот пример использования, который я повторяю снова и снова:
namespace status {
enum type {
invalid,
okay,
on_fire
};
// debug output helper
char const* to_string(type t);
}
Тип перечисления называется status::type
, который может принимать значения как status::okay
, Довольно читабельное ИМХО.