Частное пространство имен в исходных файлах

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

Suspect.h

namespace Suspect {
  /**
  *  \brief This should do this and that and more funny things.
  */
  void VerbalKint(void);  // This is for you to use
}

Suspect.cpp

namespace Suspect {
  namespace Surprise {
    /**
    * \brief The user doesn't need to be aware of this, as long 
    *        the public available VerbalKint does what it should do.
    */
    void KeyserSoze(void) {
      // Whatever
    }
  } // end Surprise

  void VerbalKint(void) {
    Surprise::KeyserSoze();
  }
}

Итак, этот макет работает. При включении Suspect.h отображается только VerbalKint. Это может быть достигнуто также с использованием класса и маркировки VerbalKint как static:

class Suspect {
public:
  // Whatever
  static void VerbalKint(void);
private:
  static void KeyserSoze(void);
};

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

Каковы ваши мысли?

Ответ 1

Если функции "свободные", вы должны использовать анонимное пространство имен в *.cpp:

namespace Suspect {
namespace Surprise {
namespace {
    void KeyserSoze(void) {
      // Whatever
    }
} // end anon
} // end Surprise
} // end Suspect

или даже:

namespace {
    void KeyserSoze(void) {
      // Whatever
    }
} // end anon

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

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

Ответ 2

Лучший подход - определить все вспомогательные функции в неназванном пространстве имен в Suspect.cpp, а не в пространстве имен Suspect::Surprise.

В вашем случае это будет:

namespace{
void KeyserSoze(){ ... };
}

Вы можете просто вызвать KeyserSoze без каких-либо спецификаторов пространства имен из Suspect.cpp.

Вы можете найти более подробную информацию об этом здесь: Без имени/анонимного пространства имен или статические функции

Другой альтернативой является объявление KeyserSoze как static, но это не рекомендуется стандартом. Стандарт С++ читается в разделе 7.3.1.1. Пространства имен, абзац 2:

Использование статического ключевого слова устарело при объявлении объектов в области пространства имен, пространство имен без имени обеспечивает превосходную альтернативу

Ответ 3

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

В С++ механизм скрыть символы, объявленные на уровне файла:

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

Например:

// Suspect.cpp

namespace Suspect {

    static void KeyserSore() {}

    void VerbalKing() { KeyserSore(); }

}

Ответ 4

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

Это: хиджры

namespace Fred {
   void Somefunc();
}

b.h

namespace Fred {
   void Anotherfunc();
}

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

c.h

namespace Fred {
   void Thirdfunc();
}

d.h

namespace Fred {
   bool Thirdfunc();
}

который все отлично и денди, пока вы не запустите программу...

Это, хотя и невозможно, гораздо менее вероятно с классами.

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