«Вспомогательные» функции в C ++ - PullRequest
28 голосов
/ 17 сентября 2008

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

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

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

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

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

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

Ответы [ 7 ]

37 голосов
/ 17 сентября 2008

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

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

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

    namespace LittleEndianHelper {
       void Function();
    }
    namespace BigEndianHelper {
       void Function();
    }
    
    #if powerpc
       namespace Helper = BigEndianHelper;
    #elif intel
       namespace Helper = LittleEndianHelper;
    #endif
    
11 голосов
/ 18 сентября 2008

Случай, когда можно использовать 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;
}
6 голосов
/ 17 сентября 2008

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

//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 ++) структурами.

4 голосов
/ 17 сентября 2008

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

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

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

3 голосов
/ 20 декабря 2008

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

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

3 голосов
/ 17 сентября 2008

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

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

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

2 голосов
/ 18 сентября 2008

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

...