2011-12-30 11 views
77

Probabilmente un duplicato, ma non è un compito facile per la ricerca di ...modo corretto definire C++ metodi dello spazio dei nomi in file cpp

dato un colpo di testa come:

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

ho visto method() definita in vari modi nel file cpp:

Versione 1:

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

Vers ione 2:

using namespace ns1; 

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

Versione 3:

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

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

+0

Nella maggior parte dei codici di solito vedi terza versione (ma io non perché: D), seconda versione è semplicemente contrario al motivo per cui vengono introdotti i namespace immagino. –

+1

Come in un dato interessante, gli strumenti di refactoring di Visual Assist sono felici di generare codice utilizzando il numero 1 o il numero 3, a seconda dello stile già in uso nel file di destinazione. –

risposta

36

versione 2 non è chiara e non è facile da capire perché non si sa quale spazio dei nomi MyClass appartiene ed è solo illogico (funzione di classe non nello stesso spazio dei nomi?)

Versione 1 è giusto perché dimostra che nello spazio dei nomi, stai definendo la funzione.

versione 3 è giusto anche perché si è utilizzato l'operatore di risoluzione :: scopo di fare riferimento alla MyClass::method() nello spazio dei nomi ns1. Preferisco la versione 3.

Vedere Namespaces (C++). Questo è il modo migliore per farlo.

+0

puoi elaborare cosa c'è che non va nella versione 2 con l'esempio? –

+13

Chiamare il n. 2 "sbagliato" è un'enorme esagerazione. Con questa logica, tutti i nomi di simboli sono "sbagliati" perché possono potenzialmente nascondere altri nomi di simboli in altri ambiti. – tenfour

+0

È illogico. Compilerà bene (scusate, male 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

4

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

2

Tutte le vie sono giuste e ognuna ha i suoi vantaggi e svantaggi.

Nella versione 1, si ha 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 namespace.

Nella versione 2, si rende più pulito il codice, ma se nel CPP è implementato più di uno spazio dei nomi, è possibile accedere direttamente alle funzioni e alle variabili dell'altro, rendendo il proprio spazio dei nomi inutile (per quel file cpp).

Nella versione 3, dovrai digitare altro e le tue linee di funzione potrebbero essere più grandi dello schermo, il che è negativo per gli effetti di progettazione.

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

E 'in questo modo:

#define OPEN_NS1 namespace ns1 { 
#define CLOSE_NS1 } 

OPEN_NS1 

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

CLOSE_NS1 

Sta a voi scegliere quale è migliore per ogni situazione =]

+12

Non vedo alcun motivo per utilizzare una macro qui. Se non vuoi rientrare, non indentare. L'uso di una macro rende il codice meno ovvio. – tenfour

+1

Penso che l'ultima versione che hai citato sia utile ogni volta che vuoi compilare il tuo codice con vecchi compilatori che non supportano gli spazi dei nomi (Sì, alcuni dinosauri sono ancora in circolazione). In tal caso puoi inserire la macro all'interno di una clausola '# ifdef'. –

+0

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 autoidentificare. Se non usi i define, perderai questa funzionalità. Inoltre, non penso che OPEN_ (spazio dei nomi) e CLOSE_ (spazio dei nomi) siano meno ovvi, se lo avete nel vostro standard di codifica. Anche la motivazione di @LucaMartini è interessante. –

1

scelgo Num.3 (anche noto come la versione verbose). È più digitante, ma l'intento è preciso per te e per il compilatore.Il problema che hai postato così com'è è in realtà più semplice del mondo reale. Nel mondo reale ci sono altri scopi per le definizioni, non solo per 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 un namespace usando questo approccio, altrimenti la tua inline potrebbe essere sostituita tramite ODR. Ne avrai bisogno per alcune definizioni (in particolare, le specializzazioni dei modelli).

Num.2 Questo è molto fragile, in particolare nelle codebase di grandi dimensioni, poiché le intestazioni e le dipendenze cambiano, il programma non riuscirà a compilare.

Num.3 Questo è l'ideale, ma molto da digitare: quale è l'intento di definire qualcosa. Questo fa esattamente questo, e il compilatore interviene per assicurarsi di non aver commesso un errore, una definizione non è sfasata con la sua dichiarazione, ecc.

12

Sto usando la versione 4 (sotto) perché combina la maggior parte dei vantaggi della versione 1 (tersezza della definizione resoettiva) e della versione 3 (essere al massimo esplicito). Il principale svantaggio è che le persone non ci sono abituate ma, dal momento che la considero tecnicamente superiore alle alternative, non mi dispiace.

Versione 4: utilizzare piena qualificazione utilizzando alias dello spazio dei nomi:

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

Nel mio mondo che sto usando frequentemente alias dello spazio dei nomi come tutto ciò che è esplicitamente qualificato - a meno che non può (ad esempio i nomi delle variabili) o è un noto punto di personalizzazione (es. swap() in un modello di funzione).

+1

Mentre penso che la logica "è meglio quindi non mi importa se si confonde la gente" è imperfetto, io sono d'accordo questo _is_ un buon approccio per i namespace annidati. –

+1

+1 per l'eccellente idea del "perché-non-penso-di-quello"! (Per quanto riguarda "le persone non sono abituate a [nuove cose tecnicamente superiori]", si abitueranno ad esso se più persone lo fanno.) – wjl

3

Si scopre che non è solo "materia di stile di codifica". Num. 2 porta a un errore di collegamento durante la definizione e l'inizializzazione di una variabile dichiarata extern nel file di intestazione. Dai un'occhiata all'esempio nella mia domanda. Definition of constant within namespace in cpp file

13

cinque anni più tardi e ho pensato di ricordare questo, che sia sembra piacevole e non è il male

using ns1::MyClass; 

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

Questa è la migliore risposta. Sembra il più pulito ed evita i problemi con OP's Versions 1, che può portare le cose nello spazio dei nomi in modo non intenzionale, e 2, che può portare le cose nello spazio globale involontariamente. –

Problemi correlati