Статическая функция без элемента или статическая функция без элемента? - PullRequest
8 голосов
/ 05 августа 2011

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

Возможные варианты:

1) Статическая функция в классе / структуре помощника.

struct helper
{
    static bool doSomething(...);
};

2) Функция без члена.

namespace helper
{
    bool doSomething(...);
}

3) Статическая функция без членов.

namespace helper
{
    static bool doSomething(...);
}

В некоторых случаях может потребоваться инициализация или сохранение состояния в «утилите», поэтому я перехожу к варианту 1, чтобы избежать «глобального» состояния.Однако, если нет состояния, которое необходимо сохранить, должен ли я тогда выбрать вариант 2 или 3?В чем практическая разница между вариантом 2 и 3?

Что важно рассмотреть и есть ли предпочтительный способ , чтобы приблизиться к этому?Спасибо!

Ответы [ 3 ]

12 голосов
/ 05 августа 2011

Разница между вариантами 2 и 3 заключается в том, что во втором случае функция будет внутренней по отношению к единице перевода. Если функция определена только в cpp, это должна быть опция (которая приблизительно эквивалентна безымянному пространству имен - что является четвертым вариантом для рассмотрения, опять же примерно эквивалентным 3).

Если функция будет использоваться различными единицами перевода, вам следует перейти к варианту 2. Функция будет скомпилирована один раз (если вы не пометите ее как inline и не предоставите определение в заголовке), тогда как с опцией 3 компилятор создаст свою внутреннюю копию в каждой единице перевода.

Что касается варианта 1, я бы его избежал. В Java или C # вы вынуждены использовать классы повсюду, и в итоге вы получаете служебные классы , когда операции плохо сопоставляются с парадигмой объекта. В C ++, с другой стороны, вы можете предоставлять эти операции в виде автономных функций, и нет необходимости добавлять дополнительный слой. Если вы выбрали служебный класс , не забудьте отключить создание объектов.

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

double do_maths( double x ) {
   using namespace math;
   return my_sqrt( x ) * cube_root(x);
}
// alternatively with an utility class:
double do_maths( double x ) {
   return math::my_sqrt(x) * math::cube_root(x);
}

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

8 голосов
/ 05 августа 2011

Не используйте классы как пространства имен. Использование бесплатной (т.е. не принадлежащей) функции внутри пространства имен - это простейшая вещь .

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

Использование классов в качестве пространства имен глупо: зачем создавать класс, создание которого вы не будете создавать?

Если нужно сохранить какое-то состояние, то где-то есть глобальное состояние: статическая переменная внутри функции, глобальный объект утилиты (возможно, в «частном» пространстве имен или в качестве статической глобальной переменной в одном из ваших модулей перевода). Не пишите классы, которые вы не создаете.

Увы, люди с фоном C # / Java привыкли делать эту глупость, но это потому, что их разработчики языка в одностороннем порядке решили, что бесплатные функции - это зло. Должны ли они быть застрелены или нет, это вопрос религии.

В качестве последнего слова предостережения: глобальное состояние следует использовать редко. Действительно, он связывает ваш код с существованием глобального состояния таким образом, что вы не можете контролировать его рост. Вы должны всегда спрашивать себя, почему вы не делаете эту связь явной.

0 голосов
/ 05 августа 2011

Я использую struct со статическими функциями, потому что она предлагает чуть лучшую изоляцию, чем функции, не являющиеся членами, в пространствах имен (благодаря исключению поиска по Кенигу).

Чтобы привести пример того, чего я хочу избежать:

namespace Foo
{
      struct A
      {
      };

      void f(const A& a) {}
}

void f(const Foo:A& a) { std::cout << "AAA"; }

int main(void)
{
      Foo::A a;
      f(a); // calls Foo:f
      return 0;
}
...