Подтвердить что ты не робот

Частную статическую функцию-член или свободную функцию в анонимном пространстве имен?

Недавно я сделал стилистическое изменение и хотел увидеть, как другие программисты на C++ чувствовали к нему и были ли какие-то недостатки.

По сути, когда мне нужна функция утилиты, которая не нуждается в доступе к данному члену класса, то, что я делал, было примерно таким:

file.h

class A {
public:
    // public interface
private:
    static int some_function(int);
};

file.cpp

int A::some_function(int) {
    // ...
}

Но в последнее время я предпочитаю делать что-то большее:

file.cpp

namespace {
    int some_function(int) {

    }
}
// the rest of file.cpp

Вот мой мыслительный процесс:

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

Последний является самым убедительным для меня. Поэтому мой вопрос: есть ли какие-то недостатки?

Они функционально эквивалентны для большинства целей, о которых я могу думать. Мне кажется, что функция private static почти всегда может быть преобразована в свободную функцию в анонимном namespace.

EDIT. Одна вещь, которая приходит на ум, состоит в том, что функция private static имела бы доступ к private членам, если указана указатель на объект для работы, но если это произойдет, почему бы не сделать его членом static?

Что вы, ребята, думаете?

4b9b3361

Ответ 1

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

Как вы говорите, он уменьшает зависимости и может сэкономить некоторые перекомпилированные файлы.

Ответ 2

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

Ответ 3

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

Ответ 4

Лично, единственными статическими функциями, которые я использую, являются factory функции, такие как

class Angle {
public:
    static Angle FromDegree (float v);
    static Angle FromRadians (float v);

    ...
private:
    Angle (float degree);
};

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

Статья Скотта Мейера "Как функции, не связанные с членством, улучшают инкапсуляцию" примерно описывает тот же подход:

Минимальность и инкапсуляция

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

Конечно, интерфейс минимального класса не обязательно является лучшим интерфейсом. Я заметил в Effective С++, что добавление функций, выходящих за рамки действительно необходимых, может быть оправданным, если оно значительно улучшает производительность класса, упрощает использование класса или предотвращает вероятные ошибки клиента. Основываясь на своей работе с различными типами струн, Джек Ривз заметил, что некоторые функции просто не "чувствуют" себя правильно, когда становятся нечленами, даже если они могут быть не-друзьями, не являющимися членами. "Лучший" интерфейс для класса можно найти только путем балансировки многих конкурирующих задач, из которых степень инкапсуляции является лишь одной.

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

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

Ответ 5

Частные статические неконстантные переменные бесполезны для любого кода, а не в .cpp, поэтому я всегда использую такое анонимное пространство имен.

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

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

Ответ 6

Если вы используете сборку единства (т.е. # включая все .cpp файлы проекта в один блок компиляции, чтобы ускорить время компиляции), вы рискуете столкновениями имен с другими функциями в анонимных пространствах имен.

Что касается единственного недостатка, который я нашел.

Ответ 7

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

свободная функция:

void f() { /* cannot access private members of any class */ }

статическая функция:

class A {
public:
   static void g() { /* can access private members of class A */ }
};

функции друга:

    class A {
    public:
       friend void h();
    };
    class B {
    public:
       friend void h();
   };
   void h() { /* can access private members of both class A and class B */ }

Реальная проблема с тем, как С++ делает это, что вызов функции выглядит иначе:

int main() {
   f();
   A::g();
   h();
 }