C ++: Namespaces - Come utilizzare correttamente i file di intestazione e di origine?


90

Considera una coppia di due file sorgente: un file di dichiarazione dell'interfaccia ( *.ho *.hpp) e il suo file di implementazione ( *.cpp).

Lascia che il *.hfile sia come il seguente:

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

Ho visto due diverse pratiche per l'utilizzo degli spazi dei nomi nei file sorgente:

*.cpp mostrando la pratica n. 1:

#include "MyClass.h"
using namespace MyNamespace;

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

*.cpp mostrando la pratica n. 2:

#include "MyClass.h"
namespace MyNamespace {

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

}

La mia domanda: ci sono differenze tra queste due pratiche ed una è considerata migliore dell'altra?


30
C'è anche l'opzione 3: solo noi il nome completo, ad es int MyNamespace::MyClass::foo() ....
Benjamin Bannier


@Dave non duplicare. Queste domande si completano a vicenda. Consiglia di aggiungere il link fornito da Dave come "Leggi anche ..." a questa domanda. La mia domanda aiuterà i principianti a scegliere lo stile corretto.
nickolay

Risposte:


65

Dal punto di vista della leggibilità del codice, è probabilmente meglio a mio avviso utilizzare il metodo n. 2 per questo motivo:

Puoi essere usingpiù spazi dei nomi alla volta e qualsiasi oggetto o funzione scritta sotto quella riga può appartenere a uno qualsiasi di questi spazi dei nomi (salvo conflitti di denominazione). Il wrapping dell'intero file in un namespaceblocco è più esplicito e consente di dichiarare nuove funzioni e variabili che appartengono a quello spazio dei nomi anche all'interno del file .cpp


La domanda che Dave ha collegato nel suo commento alla tua domanda delinea anche alcuni punti chiave nelle differenze (se presenti) tra i due metodi che stai osservando
Dan F

Ragazzi, davvero non so quale risposta scegliere. Hanno intersezione mentre si completano a vicenda.
nickolay

Basta commentare per riconoscere che alcuni IDE come CLion rileveranno le implementazioni solo se si utilizza l'opzione / practice # 2.
pedrostanaka

@PedroTanaka è ancora così? Non ho notato alcun problema del genere.
John McFarlane

@JMcF Non ho controllato da quando ho pubblicato il commento. Nelle prime versioni di Clion il problema si verificava.
pedrostanaka

52

La più chiara è l'opzione che non hai mostrato:

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

È anche molto prolisso; troppo per la maggior parte delle persone. Poiché using namespaceè un ricordo per i conflitti di nome, almeno nella mia esperienza, e dovrebbe essere evitato tranne in ambiti e luoghi molto limitati, generalmente uso il tuo # 2.


4
Grazie molto chiaro. Insieme abbiamo creato una buona pagina delle FAQ per gli utenti dei namespace. :)
nickolay

2
Ragazzi, davvero non so quale risposta scegliere. Hanno intersezione mentre si completano a vicenda.
nickolay

10

Ci sono differenze tra queste due pratiche

Sì. # 1 e # 2 sono esempi rispettivamente di una direttiva using e di una definizione di spazio dei nomi . In questo caso sono effettivamente gli stessi ma hanno altre conseguenze. Ad esempio, se si introduce un nuovo identificatore a fianco MyClass::foo, avrà un ambito diverso:

# 1:

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

# 2:

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

uno è considerato migliore dell'altro?

# 1 Pro: un po 'più conciso; più difficile introdurre accidentalmente qualcosa inMyNamespace inconsapevolmente. Contro: può inserire gli identificatori esistenti involontariamente.

# 2 Pro: più chiaro che le definizioni degli identificatori esistenti e le dichiarazioni di nuovi identificatori appartengono entrambe MyNamespace. Contro: più facile introdurre involontariamente identificatori MyNamespace.

Una critica sia al # 1 che al # 2 è che si riferiscono a un intero spazio dei nomi quando probabilmente ti interessa solo la definizione dei membri di MyNamespace::MyClass. Questo è pesante e comunica male l'intento.

Una possibile alternativa al n. 1 è una dichiarazione using che include solo l'identificatore a cui sei interessato:

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

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

5

Vorrei anche aggiungere che se per qualche motivo decidi di implementare una specializzazione di template in un file cpp e using namespacefai affidamento su di te ti imbatterai nel seguente problema:

// .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(){}

Altrimenti, se applichi il metodo n. 2, questo andrà bene.


0

Vorrei aggiungere un altro modo, usando la dichiarazione using :

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

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

In questo modo ti evita di digitare il nome dello spazio dei nomi molte volte se la classe ha molte funzioni

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.