C ++: Простори імен - Як правильно використовувати файли заголовків та джерел?


88

Розглянемо пару з двох вихідних файлів: файл декларації інтерфейсу ( *.hабо *.hpp) та файл його реалізації ( *.cpp).

Нехай *.hфайл буде таким:

namespace MyNamespace {
  class MyClass {
  public:
    int foo();
  };
}

Я бачив дві різні практики використання просторів імен у вихідних файлах:

*.cpp показ практики №1:

#include "MyClass.h"
using namespace MyNamespace;

int MyClass::foo() { ... }

*.cpp показ практики №2:

#include "MyClass.h"
namespace MyNamespace {

  int MyClass::foo() { ... }

}

Моє запитання: Чи є якісь відмінності між цими двома практиками і чи одна вважається кращою за іншу?


30
Існує також варіант 3: Просто введіть повне ім’я, наприклад int MyNamespace::MyClass::foo() ....
Бенджамін Баньє,

1
Можливий дублікат: stackoverflow.com/questions/7789163/…
Девід

@Dave не дублювати. Ці питання доповнюють одне одного. Рекомендуємо додати до цього запитання посилання, надане Дейвом, як "Читайте також ...". Моє запитання допоможе новачкам вибрати правильний стиль.
nickolay

Можливий дублікат: stackoverflow.com/questions/8210935 / ...
Firedragon

Відповіді:


62

З точки зору читабельності коду, на мій погляд, краще використовувати метод №2 з цієї причини:

Одночасно може бути usingдекілька просторів імен, і будь-який об’єкт або функція, записані під цим рядком, можуть належати до будь-якого з цих просторів імен (заборона конфліктів імен). Обгортання всього файлу в namespaceблок є більш явним і дозволяє оголосити нові функції та змінні, що належать до цього простору імен, у файлі .cpp


Питання, яке Дейв зв’язав у своєму коментарі з вашим запитанням, також окреслює деякі ключові моменти у відмінностях (якщо такі є) між двома методами, які ви розглядаєте
Ден Ф,

Хлопці, я справді не знаю, чию відповідь вибрати. Вони мають перетин, доповнюючи один одного.
nickolay

Просто коментуючи, щоб визнати, що деякі IDE, такі як CLion, виявлятимуть реалізації, лише якщо ви використовуєте опцію / практику №2.
PedroTanaka

@PedroTanaka це все ще так? Я не помітив жодної такої проблеми.
Джон Макфарлейн,

@JMcF Я не перевіряв з часу опублікування коментаря. У ранніх версіях Clion виникла проблема.
PedroTanaka

51

Найяскравіший варіант, який ви не показували:

int MyNamespace::MyClass::foo()
{
    //  ...
}

Це також дуже багатослівно; занадто для більшості людей. Оскільки using namespaceце рецепт конфліктів імен, принаймні на моєму досвіді, і його слід уникати, за винятком дуже обмежених областей та місць, я, як правило, використовую ваш номер 2


3
Дякую дуже чітко. Разом ми створили хорошу сторінку поширених запитань для користувачів просторів імен. :)
nickolay

2
Хлопці, я справді не знаю, чию відповідь вибрати. Вони мають перетин, доповнюючи один одного.
nickolay

10

Чи є відмінності між цими двома практиками?

Так. №1 та №2 - приклади директиви using та визначення простору імен відповідно. У цьому випадку вони фактично однакові, але мають інші наслідки. Наприклад, якщо ви введете поряд новий ідентифікатор MyClass::foo, він матиме інший обсяг:

# 1:

using namespace MyNamespace;
int x;  // defines ::x

№2:

namespace MyNamespace {
  int x;  // defines MyNamespace::x
}

чи один вважається кращим за інший?

# 1 Плюси: трохи більш лаконічний; важче випадково ввести щось MyNamespaceмимоволі. Мінуси: може ненавмисно втягнути існуючі ідентифікатори.

№2 Плюси: чіткіше, що визначення існуючих ідентифікаторів та декларації нових ідентифікаторів належать MyNamespace. Мінуси: легше ненавмисно вводити ідентифікатори MyNamespace.

Критика як №1, так і №2 полягає в тому, що вони мають на увазі цілий простір імен, коли вас, мабуть, цікавить лише визначення членів MyNamespace::MyClass. Це важко, і це погано повідомляє про наміри.

Можлива альтернатива # 1 - декларація using, яка включає лише ідентифікатор, який вас цікавить:

#include "MyClass.h"
using MyNamespace::MyClass;

int MyClass::foo() { ... }

4

Я також хотів би додати, що якщо ви з якихось причин вирішите реалізувати спеціалізацію шаблону у файлі cpp і просто покладаєтесь, using namespaceви зіткнетеся з такою проблемою:

// .h file
namespace someNameSpace
{
  template<typename T>
    class Demo
    {
      void foo();
    };
}

// .cpp file
using namespace someNameSpace;

template<typename T>
void Demo<T>::foo(){}

// this will produce
// error: specialization of 'template<class T> void someNameSpace::Demo<T>::foo()' in different namespace [-fpermissive]
template<>
void Demo<int>::foo(){}

В іншому випадку, якщо ви застосуєте метод №2, це буде добре.


0

Я хотів би додати ще один спосіб, використовуючи декларацію using :

#include "MyClass.h"
using MyNamespace::MyClass;

int MyClass::foo() { ... }

Цей спосіб позбавляє вас від набору назви простору імен багато часу, якщо клас має багато функцій

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.