Modo corretto per definire i metodi dello spazio dei nomi C ++ nel file .cpp


108

Probabilmente un duplicato, ma non facile da cercare ...

Data un'intestazione come:

namespace ns1
{
 class MyClass
 {
  void method();
 };
}

Ho visto method()definito in diversi modi nel file .cpp:

Versione 1:

namespace ns1
{
 void MyClass::method()
 {
  ...
 }
}

Versione 2:

using namespace ns1;

void MyClass::method()
{
 ...
}

Versione 3:

void ns1::MyClass::method()
{
 ...
}

C'è un modo "giusto" per farlo? Qualcuno di questi "sbagliati" in quanto non significano tutti la stessa cosa?


Nella maggior parte dei codici di solito vedo la terza versione (ma non so perché: D), la seconda versione è semplicemente l'opposto del motivo per cui gli spazi dei nomi vengono introdotti immagino.
signor Anubis il

1
Come in un fatto interessante, gli strumenti di refactoring di Visual Assist sono felici di generare codice utilizzando # 1 o # 3, a seconda dello stile già in uso nel file di destinazione.
Mr. Boy

Risposte:


51

La versione 2 non è chiara e non è facile da capire perché non sai a quale spazio dei nomi MyClassappartiene ed è semplicemente illogica (la funzione di classe non è nello stesso spazio dei nomi?)

La versione 1 è corretta perché mostra che nello spazio dei nomi stai definendo la funzione.

La versione 3 è corretta anche perché hai utilizzato l' ::operatore di risoluzione dell'ambito per fare riferimento a MyClass::method ()nello spazio dei nomi ns1. Preferisco la versione 3.

Vedere Spazi dei nomi (C ++) . Questo è il modo migliore per farlo.


22
Definire il numero 2 "sbagliato" è un'enorme esagerazione. In base a questa logica, tutti i nomi dei simboli sono "sbagliati" perché possono potenzialmente nascondere altri nomi di simboli in altri ambiti.
tenfour

È illogico. Verrà compilato bene (scusate, misex lo ha spiegato nella risposta), ma perché dovreste definire una funzione al di fuori del suo spazio dei nomi? Confonde il lettore. Inoltre, quando vengono utilizzati molti spazi dei nomi, non mostra a quale spazio dei nomi appartiene MyClass. Le versioni 1 e 3 risolvono questo problema. In conclusione, non è sbagliato, ma solo poco chiaro e confuso.
GILGAMESH

3
Sono d'accordo con @PhoenicaMacia, il trucco dell'uso è orribile e può creare confusione. Considera una classe che implementa un operatore come una funzione libera, nell'intestazione avresti namespace N {struct X { void f(); }; X operator==( X const &, X const & ); }, ora nel file cpp con l' istruzione using puoi definire la funzione membro come void X::f() {}, ma se definisci definirai X operator==(X const&, X const&)un operatore diverso da quello definito nell'intestazione (dovrai usare 1 o 3 per la funzione libera lì).
David Rodríguez - dribeas

1
In particolare preferisco 1, e l'esempio nell'articolo collegato non risolve davvero nulla, il primo esempio usa 1, il secondo usa un mix di 1 e 3 (le funzioni sono definite con qualifica, ma sono definite all'interno dello spazio dei nomi esterno)
David Rodríguez - dribeas

1
Dei 3 direi 1) è il migliore, tuttavia la maggior parte degli IDE ha l'abitudine piuttosto fastidiosa di indentare tutto all'interno della dichiarazione dello spazio dei nomi e ciò aggiunge un po 'di confusione.
locka

28

5 anni dopo e ho pensato di menzionarlo, che sembra carino e non è malvagio

using ns1::MyClass;

void MyClass::method()
{
  // ...
}

3
Questa è la migliore risposta. Sembra il più pulito ed evita i problemi con le versioni di OP 1, che potrebbero portare le cose nello spazio dei nomi involontariamente, e 2, che potrebbero portare le cose nello spazio globale involontariamente.
ayane_m

Sì, questa è un'ottima combinazione di meno digitazione di 3, pur dichiarando esplicitamente l'intento.
jb

14

Sto usando la versione 4 (sotto) perché combina la maggior parte dei vantaggi della versione 1 (lacune della definizione risettiva) e della versione 3 (sii massimamente esplicita). Lo svantaggio principale è che le persone non ci sono abituate ma siccome lo considero tecnicamente superiore alle alternative non mi dispiace.

Versione 4: utilizza la qualifica completa utilizzando gli alias dello spazio dei nomi:

#include "my-header.hpp"
namespace OI = outer::inner;
void OI::Obj::method() {
    ...
}

Nel mio mondo utilizzo frequentemente alias dello spazio dei nomi poiché tutto è esplicitamente qualificato, a meno che non sia possibile (ad es. Nomi di variabili) o non sia un punto di personalizzazione noto (ad es. Swap () in un modello di funzione).


1
Anche se penso che la logica "è meglio quindi non mi interessa se confonde le persone" è difettosa, devo ammettere che questo è un buon approccio per gli spazi dei nomi annidati.
Mr. Boy

1
+1 per l'eccellente idea del "perché non ci ho pensato"! (Per quanto riguarda "le persone non sono abituate a [cose nuove tecnicamente superiori]", si
abitueranno

Solo per essere sicuro di aver capito, sono già entrambi outere innerdefiniti come spazi dei nomi in altri file di intestazione?
dekuShrub

4

La versione 3 rende l'associazione tra la classe e lo spazio dei nomi molto esplicita a scapito di una maggiore digitazione. La versione 1 lo evita ma acquisisce l'associazione con un blocco. La versione 2 tende a nasconderlo, quindi eviterei quello.



3

Scelgo Num.3 (ovvero la versione dettagliata). È più digitando, ma l'intento è esatto per te e per il compilatore. Il problema che hai pubblicato così com'è è in realtà più semplice del mondo reale. Nel mondo reale, ci sono altri ambiti per le definizioni, non solo i membri della classe. Le tue definizioni non sono molto complicate solo con le classi, perché il loro ambito non viene mai riaperto (a differenza degli spazi dei nomi, dell'ambito globale, ecc.).

Num.1 questo può fallire con ambiti diversi dalle classi - tutto ciò che può essere riaperto. Quindi, puoi dichiarare una nuova funzione in uno spazio dei nomi usando questo approccio, oppure i tuoi inline potrebbero finire per essere sostituiti tramite ODR. Ti servirà per alcune definizioni (in particolare, le specializzazioni dei modelli).

Num.2 Questo è molto fragile, in particolare in codebase di grandi dimensioni: poiché le intestazioni e le dipendenze cambiano, il programma non verrà compilato.

Num.3 Questo è l'ideale, ma molto da scrivere - qual è il tuo intento di definire qualcosa . Questo fa esattamente questo e il compilatore interviene per assicurarsi di non aver commesso un errore, una definizione non è fuori sincronia con la sua dichiarazione, ecc.



2

Tutti i modi sono giusti e ognuno ha i suoi vantaggi e svantaggi.

Nella versione 1, hai il vantaggio di non dover scrivere lo spazio dei nomi davanti a ciascuna funzione. Lo svantaggio è che otterrai un'identificazione noiosa, specialmente se hai più di un livello di spazi dei nomi.

Nella versione 2, rendi il tuo codice più pulito, ma se hai più di uno spazio dei nomi implementato nel CPP, uno può accedere direttamente alle funzioni e alle variabili dell'altro, rendendo il tuo spazio dei nomi inutile (per quel file cpp).

Nella versione 3, dovrai digitare di più e le linee delle funzioni potrebbero essere più grandi dello schermo, il che è negativo per gli effetti di design.

C'è anche un altro modo in cui alcune persone lo usano. È simile alla prima versione, ma senza problemi di identificazione.

È così:

#define OPEN_NS1 namespace ns1 { 
#define CLOSE_NS1 }

OPEN_NS1

void MyClass::method()
{
...
}

CLOSE_NS1

Sta a te scegliere quale è meglio per ogni situazione =]


14
Non vedo alcun motivo per utilizzare una macro qui. Se non vuoi applicare il rientro, non farlo. L'uso di una macro rende il codice meno ovvio.
tenfour

1
Penso che l'ultima versione che hai menzionato sia utile ogni volta che vuoi compilare il tuo codice con vecchi compilatori che non supportano i namespace (Sì, alcuni dinosauri sono ancora in circolazione). In tal caso puoi inserire la macro all'interno di una #ifdefclausola.
Luca Martini

Non devi identificarti se non vuoi, ma se non usi le macro, alcuni IDE proveranno a farlo per te. Ad esempio, in Visual Studio è possibile selezionare l'intero codice e premere ALT + F8 per l'identificazione automatica. Se non usi le definizioni, perderai quella funzionalità. Inoltre, non penso che OPEN_ (spazio dei nomi) e CLOSE_ (spazio dei nomi) sia meno ovvio, se lo hai nel tuo standard di codifica. Interessante anche il motivo fornito da @LucaMartini.
Renan Greinert

Se questo è stato reso generico cioè #define OPEN_NS(X)penso che sia leggermente utile, ma non proprio ... Non mi oppongo alle macro ma questo sembra un po 'ECCESSIVO. Penso che l'approccio di Dietmar Kühl sia migliore per gli spazi dei nomi annidati.
Mr. Boy
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.