Вопрос

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

Это заставило меня задуматься о том, как лучше всего сгруппировать «вспомогательные» функции.Способ Java/C# заключался бы в использовании класса статических функций с частным конструктором, например:

class Helper  
{  
private:  
  Helper() { }
public:  
  static int HelperFunc1();  
  static int HelperFunc2();  
};

Однако, будучи C++, вы также можете использовать пространство имен:

namespace Helper  
{  
  int HelperFunc1();  
  int HelperFunc2();  
}

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

Это было полезно?

Решение

Накладные расходы не являются проблемой, хотя пространства имен имеют некоторые преимущества.

  • Вы можете открыть пространство имен в другом заголовке, группируя вещи более логично, сохраняя при этом низкие зависимости компиляции
  • Вы можете использовать псевдоним пространства имен в своих интересах (отладка/выпуск, Помощники, специальные платформы, ....)

    напримерЯ сделал что-то вроде

    namespace LittleEndianHelper {
       void Function();
    }
    namespace BigEndianHelper {
       void Function();
    }
    
    #if powerpc
       namespace Helper = BigEndianHelper;
    #elif intel
       namespace Helper = LittleEndianHelper;
    #endif
    

Другие советы

Случай, когда можно использовать 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;
}

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

//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);
};

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

Редактировать из paercebal

Ответ был слишком хорош, чтобы позволить ему умереть из-за ошибки перечисления (см. комментарии).Я заменил перечисления (которые могут быть объявлены вперед только в C++0x, но не в современном C++) на структуры.

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

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

У вас не будет никаких дополнительных накладных расходов, если они будут размещены в классе, а не в пространстве имен.

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

Еще одним преимуществом пространств имен является их удобочитаемость в дальнейшем.В классы вам необходимо включать такие слова, как «Помощник», чтобы позже напомнить вам, что конкретный класс не используется для создания объектов.

На практике никаких накладных расходов тоже нет.После компиляции меняется только используемое искажение имени.

Скопировал/обрезал/переработал часть моего ответа из Как правильно использовать пространства имен в 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() ;
}

Вы даже можете импортировать избранные символы из разных пространств имен, чтобы создать свой собственный интерфейс пространства имен.Мне еще предстоит найти этому практическое применение, но теоретически это круто.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top