При рефакторинге некоторого старого кода я исключил ряд общедоступных методов, которые на самом деле должны были быть статическими, поскольку они: а) не работают с какими-либо данными-членами и не вызывают какие-либо другие функции-члены и б) потому что они могут оказаться полезными в другом месте.
Это заставило меня задуматься о том, как лучше всего сгруппировать «вспомогательные» функции вместе. Способ Java / C# заключался бы в использовании класса статических функций с частным конструктором, например:
class Helper
{
private:
Helper() { }
public:
static int HelperFunc1();
static int HelperFunc2();
};
Однако, будучи C++, вы также можете использовать пространство имен:
namespace Helper
{
int HelperFunc1();
int HelperFunc2();
}
В большинстве случаев я думаю, что предпочел бы подход с использованием пространства имен, но я хотел знать, каковы плюсы и минусы каждого подхода. Если использовать, например, классовый подход, возникнут ли накладные расходы?





Основное преимущество использования пространства имен заключается в том, что вы можете снова открыть его и добавить что-нибудь позже, чего нельзя сделать с классом. Это делает этот подход лучше для слабосвязанных помощников (например, у вас может быть пространство имен Helpers для всей вашей библиотеки, как и весь STL в :: std)
Основное преимущество класса заключается в том, что вы можете вложить его внутри класса, используя его, вы не можете вложить пространство имен в класс. Это делает этот подход более подходящим для тесно связанных помощников.
У вас не будет дополнительных накладных расходов, если они будут в классе по сравнению с пространством имен.
Верно, что вы не можете вложить пространство имен в класс, но вы можете использовать анонимное пространство имен в CPP-файле класса практически с таким же эффектом (или даже именованное пространство имен, вложенное в анонимное). Но хороший ответ!
Накладные расходы не являются проблемой, хотя у пространств имен есть некоторые преимущества.
Вы можете использовать псевдоним пространства имен в своих интересах (отладка / выпуск, помощники для конкретных платформ, ....)
например Я делал такие вещи, как
namespace LittleEndianHelper {
void Function();
}
namespace BigEndianHelper {
void Function();
}
#if powerpc
namespace Helper = BigEndianHelper;
#elif intel
namespace Helper = LittleEndianHelper;
#endif
Или вы можете сказать #if powerpc \ using BigEndianHelper; \ #else ... Это исключает квалификатор при вызове функции. То, что вы не можете сделать в классе (в глобальном масштабе): Class :: func
Интересное использование для переключения пространства имен "по умолчанию", но с сохранением доступности обеих реализаций.
@joe, да, но тогда другая реализация больше не доступна. Например. Файлы фотошопа находятся внутри в основном BigEndian (независимо от платформы), вы все равно можете использовать BigEndianHelper в функциях, которые работают с файлами фотошопа, и использовать помощник по платформе в других местах (просто пример;)
Чтобы добавить к отличному ответу Питера, еще одним преимуществом пространств имен является то, что вы можете пересылать объявления, которые вы поместили в пространство имен где-то еще, особенно структуры ...
//Header a.h
// Lots of big header files, spreading throughout your code
class foo
{
struct bar {/* ... */);
};
//header b.h
#include a.h // Required, no way around it, pulls in big headers
class b
{
//...
DoSomething(foo::bar);
};
И с пространствами имен ...
//Header a.h
// Big header files
namespace foo
{
struct bar {/* ... */);
}
//header b.h
// Avoid include, instead forward declare
// (can put forward declares in a _fwd.h file)
namespace foo
{
struct bar;
}
class b
{
//...
// note that foo:bar must be passed by reference or pointer
void DoSomething(const foo::bar & o);
};
Форвардные объявления имеют большое значение для вашего времени компиляции после небольших изменений заголовка, когда вы заканчиваете проектом, охватывающим сотни исходных файлов.
Ответ был слишком хорош, чтобы позволить ему умереть из-за ошибки перечисления (см. Комментарии). Я заменил перечисления (которые могут быть объявлены вперед только в C++ 0x, а не в сегодняшнем C++) структурами.
На самом деле вы не можете пересылать объявления перечислений. Прямое объявление перечислений - это расширение Microsoft, поэтому его переносимость не гарантируется.
Я согласен с graham.reeds. вы не можете переслать перечисление, размер которого зависит от фактических значений меток перечисления
Правильное перечисление по структуре. +1 в любом случае, потому что это очень веская причина, и это потому, что я слишком много споткнулся о типе кода, использующем классы вместо пространств имен, и был вынужден сделать полное включение вместо прямого объявления.
Для всех, кого смущают предыдущие комментарии: уже около десяти лет можно пересылать объявления enum. См. здесь.
Пространства имен предлагают дополнительное преимущество поиска по Кенигу. Использование вспомогательных классов может сделать ваш код более подробным - обычно вам нужно включать имя вспомогательного класса в вызов.
Еще одно преимущество пространств имен - их удобочитаемость в дальнейшем. С классами вам нужно включать такие слова, как «Помощник», чтобы позже напомнить вам, что конкретный класс не используется для создания объектов.
На практике накладных расходов тоже нет. После компиляции меняется только название, используемое для изменения имени.
Случай, когда можно использовать class (или struct) вместо namespace, - это когда нужен тип, например:
struct C {
static int f() { return 33; }
};
namespace N {
int f() { return 9; }
}
template<typename T>
int foo() {
return T::f();
}
int main() {
int ret = foo<C>();
//ret += foo<N>(); // compile error: N is a namespace
return ret;
}
Ого ... Я ДЕЙСТВИТЕЛЬНО верю, что пространства имен лучше классов для такого рода проблем, но ваш пример - ОДИН пример, когда классы имеют ОДНО преимущество перед пространствами имен. +1.
(Обратите внимание, что я все равно буду использовать пространства имен над классами в 99,99% случаев ... но ваш комментарий - это всего лишь часть информации, которую стоит знать)
Я бы заменил «одно преимущество» на «единственное преимущество».
Я предпочитаю использовать анонимные пространства имен при создании вспомогательных функций. Поскольку они (как правило) должны быть видны только тому модулю, который о них заботится, это хороший способ контролировать зависимости.
Скопировал / обрезал / переработал часть моего ответа из Как правильно использовать пространства имен в C++?.
Вы можете использовать "using", чтобы избежать повторения "префикса" вашей вспомогательной функции. Например:
struct AAA
{
void makeSomething() ;
} ;
namespace BBB
{
void makeSomethingElse() ;
}
void willCompile()
{
AAA::makeSomething() ;
BBB::makeSomethingElse() ;
}
void willCompileAgain()
{
using BBB ;
makeSomethingElse() ; // This will call BBB::makeSomethingElse()
}
void WONT_COMPILE()
{
using AAA ; // ERROR : Won't compile
makeSomething() ; // ERROR : Won't compile
}
Пространства имен - это больше, чем просто пакеты. Другой пример можно найти в книге Бьярна Страуструпа «Язык программирования C++».
В «Специальном выпуске» в 8.2.8 Состав пространства имен он описывает, как можно объединить два пространства имен AAA и BBB в другое, называемое CCC. Таким образом, CCC становится псевдонимом для AAA и BBB:
namespace AAA
{
void doSomething() ;
}
namespace BBB
{
void doSomethingElse() ;
}
namespace CCC
{
using namespace AAA ;
using namespace BBB ;
}
void doSomethingAgain()
{
CCC::doSomething() ;
CCC::doSomethingElse() ;
}
Вы даже можете импортировать выбранные символы из разных пространств имен, чтобы создать свой собственный интерфейс пользовательского пространства имен. Мне еще предстоит найти этому практическое применение, но в теории это круто.
Я бы предложил более информативный заголовок для этого вопроса, возможно, что-то вроде: «пространство имен vs. класс для функций« Помощник »в C++»