Spazio dei nomi + funzioni rispetto ai metodi statici su una classe


290

Diciamo che ho, o ho intenzione di scrivere, una serie di funzioni correlate. Diciamo che sono legati alla matematica. A livello organizzativo, dovrei:

  1. Scrivi queste funzioni e inseriscile nel mio MyMathspazio dei nomi e consultale tramiteMyMath::XYZ()
  2. Creare una classe chiamata MyMathe rendere statici questi metodi e fare riferimento allo stesso modoMyMath::XYZ()

Perché dovrei scegliere l'uno rispetto all'altro come mezzo per organizzare il mio software?


4
per prima cosa, gli spazi dei nomi sono un'aggiunta più recente al linguaggio, rispetto alle classi e ai metodi statici, che erano nella lingua dal momento in cui era chiamato "C con classi". Alcuni programmatori potrebbero essere più a loro agio con le funzionalità precedenti. Alcuni altri programmatori potrebbero utilizzare vecchi compilatori. Solo il mio $ 0,02
Rom

21
@ Rom: hai ragione su "vecchi programmatori", ma hai torto su "vecchi compilatori". Gli spazi dei nomi sono stati compilati correttamente da eoni (ho lavorato con loro con Visual C ++ 6, risalente al 1998!). Per quanto riguarda la "C con classi", alcune persone in questo forum non sono nemmeno nate quando è successo: usare questo come argomento per evitare una funzionalità C ++ standard e diffusa è un errore. In conclusione, solo i compilatori C ++ obsoleti non supportano gli spazi dei nomi. Non usare quell'argomento come scusa per non usarli.
paercebal,

@paercebal: alcuni compilatori antichi sono ancora in uso nel mondo embedded. Non supportare gli spazi dei nomi è probabilmente uno dei più piccoli inconvenienti che si devono affrontare durante la scrittura di codice per varie piccole CPU con cui tutti interagiscono ogni giorno: stereo, microonde, unità di controllo del motore in auto, semaforo, ecc. sii chiaro: non sto sostenendo di non usare un compilatore migliore e più nuovo ovunque. Au conrare: sono tutto per le funzionalità linguistiche più recenti (tranne RTTI;)). Sto solo sottolineando che esiste una tale tendenza
Rom,

13
@Rom: Nel caso attuale, l'autore della domanda ha la scelta, quindi apparentemente, nessuno dei suoi compilatori non riesce a compilare un codice spaziale. E poiché si tratta di una domanda su C ++, è necessario fornire una risposta C ++, compresa la necessità di menzionare gli spazi dei nomi e le soluzioni RTTI al problema. Dare una risposta C, o una risposta C con classi per compilatori obsoleti è fuori tema.
Paercebal,

2
"Just my $ .02" - varrebbe di più se tu avessi fornito prove di compilatori C ++ esistenti che non supportano gli spazi dei nomi. "alcuni compilatori antichi sono ancora in uso nel mondo embedded" - questo è sciocco; il "mondo incorporato" è uno sviluppo più recente degli spazi dei nomi C ++. "C con classi" fu introdotto nel 1979, molto prima che qualcuno incorporasse il codice C in qualsiasi cosa. "Sto solo sottolineando che esiste una tale tendenza" - anche se così, non ha nulla a che fare con questa domanda.
Jim Balter,

Risposte:


243

Per impostazione predefinita, utilizzare le funzioni basate sul nome.

Le classi devono costruire oggetti, non sostituire spazi dei nomi.

Nel codice orientato agli oggetti

Scott Meyers ha scritto un intero articolo per il suo efficace libro C ++ su questo argomento, "Preferire le funzioni non amiche dei non membri alle funzioni dei membri". Ho trovato un riferimento online a questo principio in un articolo di Herb Sutter:http://www.gotw.ca/gotw/084.htm

La cosa importante da sapere è che: In C ++ le funzioni nello stesso spazio dei nomi di una classe appartengono all'interfaccia di quella classe (poiché ADL cercherà quelle funzioni quando risolve le chiamate di funzione).

Le funzioni con spazio dei nomi, se non dichiarate "amiche", non hanno accesso agli interni della classe, mentre i metodi statici hanno.

Ciò significa, ad esempio, che quando si mantiene la propria classe, se è necessario modificare gli interni della propria classe, sarà necessario cercare gli effetti collaterali in tutti i suoi metodi, compresi quelli statici.

Estensione I

Aggiunta di codice a un'interfaccia di classe.

In C #, puoi aggiungere metodi a una classe anche se non hai accesso ad essa. Ma in C ++, questo è impossibile.

Ma, sempre in C ++, puoi ancora aggiungere una funzione con spaziatura dei nomi, anche a una classe che qualcuno ha scritto per te.

Vedi dall'altra parte, questo è importante quando progetti il ​​tuo codice, perché inserendo le tue funzioni in uno spazio dei nomi, autorizzerai i tuoi utenti ad aumentare / completare l'interfaccia della classe.

Estensione II

Un effetto collaterale del punto precedente, è impossibile dichiarare metodi statici in più intestazioni. Ogni metodo deve essere dichiarato nella stessa classe.

Per gli spazi dei nomi, le funzioni dello stesso spazio dei nomi possono essere dichiarate in più intestazioni (la funzione di scambio quasi standard è il miglior esempio).

Estensione III

Il cool di base di uno spazio dei nomi è che in alcuni codici, puoi evitare di menzionarlo, se usi la parola chiave "using":

#include <string>
#include <vector>

// Etc.
{
   using namespace std ;
   // Now, everything from std is accessible without qualification
   string s ; // Ok
   vector v ; // Ok
}

string ss ; // COMPILATION ERROR
vector vv ; // COMPILATION ERROR

E puoi persino limitare l '"inquinamento" a una classe:

#include <string>
#include <vector>

{
   using std::string ;
   string s ; // Ok
   vector v ; // COMPILATION ERROR
}

string ss ; // COMPILATION ERROR
vector vv ; // COMPILATION ERROR

Questo "modello" è obbligatorio per l'uso corretto del linguaggio di scambio quasi standard.

E questo è impossibile da fare con i metodi statici nelle classi.

Quindi, gli spazi dei nomi C ++ hanno la loro semantica.

Ma va oltre, poiché puoi combinare gli spazi dei nomi in un modo simile all'eredità.

Ad esempio, se si dispone di uno spazio dei nomi A con una funzione AAA, uno spazio dei nomi B con una funzione BBB, è possibile dichiarare uno spazio dei nomi C e portare AAA e BBB in questo spazio dei nomi con la parola chiave utilizzando.

Conclusione

Gli spazi dei nomi sono per gli spazi dei nomi. Le lezioni sono per lezioni.

Il C ++ è stato progettato in modo che ogni concetto sia diverso e viene utilizzato in modo diverso, in casi diversi, come soluzione a problemi diversi.

Non usare le classi quando hai bisogno di spazi dei nomi.

E nel tuo caso, hai bisogno di spazi dei nomi.


Questa risposta può essere applicata anche ai thread, ovvero è meglio usare spazi dei nomi piuttosto che metodi statici per i thread?
dashesy

3
@dashesy: ​​gli spazi dei nomi rispetto ai metodi statici non hanno nulla a che fare con i thread, quindi sì, gli spazi dei nomi sono migliori perché gli spazi dei nomi sono quasi sempre migliori dei metodi statici. Se una cosa, i metodi statici hanno accesso alle variabili dei membri della classe, quindi hanno in qualche modo un valore di incapsulamento inferiore rispetto agli spazi dei nomi. E isolare i dati è ancora più importante nell'esecuzione thread.
Paercebal,

@ paercebal- grazie, stavo usando i metodi di classe statica per le funzioni di thread. Ora capisco che stavo abusando della classe come spazio dei nomi, quindi quale pensi sia l'approccio migliore per avere più thread in un oggetto? Ho fatto questa domanda anche su SO, apprezzo se puoi far luce (qui o nella domanda stessa)
preciso

1
@dashesy: ​​stai chiedendo problemi. Quello che vuoi con diversi thread è isolare i dati che non dovrebbero essere condivisi, quindi avere più thread con accesso privilegiato ai dati privati ​​di una classe è una cattiva idea. Vorrei nascondere un thread all'interno di una classe e assicurarmi di isolare i dati per quel thread dai dati per il thread principale. Naturalmente, i dati che dovrebbero essere condivisi possono essere membri di quella classe, ma dovrebbero comunque essere sincronizzati (blocchi, atomici, ecc.). Non sono sicuro di quante librerie hai accesso, ma usare task / async è ancora meglio.
Paercebal,

la risposta di paercebal dovrebbe essere quella accettata! Solo un altro collegamento per lo swap quasi standard () tramite namespace + ADL -> stackoverflow.com/questions/6380862/...
Gob00st

54

Ci sono molte persone che non sarebbero d'accordo con me, ma è così che lo vedo:

Una classe è essenzialmente una definizione di un certo tipo di oggetto. I metodi statici dovrebbero definire operazioni intimamente legate alla definizione dell'oggetto.

Se stai per avere un gruppo di funzioni correlate non associate a un oggetto sottostante o definizione di un tipo di oggetto , direi che vai solo con uno spazio dei nomi. Solo per me, concettualmente, questo è molto più sensato.

Ad esempio, nel tuo caso, chiediti "Cos'è un MyMath?" Se MyMathnon definisce un tipo di oggetto, poi mi direi: non ce la fanno una classe.

Ma come ho detto, so che ci sono molte persone che (anche con veemenza) non sarebbero d'accordo con me su questo (in particolare, gli sviluppatori Java e C #).


3
Hai una prospettiva molto pura su questo. Ma in pratica, una classe con metodi completamente statici può tornare utile: puoi typedefusarli come parametri modello, ecc.
Shog9

56
Questo perché Jave e C # non hanno scelta.
Martin York,

7
@ shog9. Puoi anche modellare le funzioni!
Martin York,

6
@Dan: presumibilmente, uno che aveva bisogno di routine matematiche e voleva supportare "collegare" diverse implementazioni.
Shog9

1
@Dan: "Penso che se qualcuno è interessato a usare una classe come parametro template, quella classe sta quasi sicuramente definendo un oggetto sottostante." No, per niente. Pensa ai tratti. (Tuttavia, sono pienamente d'accordo con la tua risposta.)
sbi

18
  • Se sono necessari dati statici, utilizzare metodi statici.
  • Se sono funzioni modello e desideri essere in grado di specificare un insieme di parametri modello per tutte le funzioni, utilizza i metodi statici in una classe modello.

In caso contrario, utilizzare le funzioni spaziate dal nome.


In risposta ai commenti: sì, i metodi statici e i dati statici tendono ad essere sovrautilizzati. Ecco perché ho offerto solo due scenari correlati in cui penso che possano essere utili. Nell'esempio specifico del PO (un insieme di routine matematiche), se voleva la capacità di specificare parametri - diciamo, un tipo di dati di base e precisione dell'output - che sarebbero stati applicati a tutte le routine, avrebbe potuto fare qualcosa del tipo:

template<typename T, int decimalPlaces>
class MyMath
{
   // routines operate on datatype T, preserving at least decimalPlaces precision
};

// math routines for manufacturing calculations
typedef MyMath<double, 4> CAMMath;
// math routines for on-screen displays
typedef MyMath<float, 2> PreviewMath;

Se non avete bisogno di questo, poi con tutti i mezzi utilizzare uno spazio dei nomi.


2
i cosiddetti dati statici possono essere dati a livello di spazio dei nomi nel file di implementazione dello spazio dei nomi, ciò riduce ulteriormente l'accoppiamento poiché non deve essere visualizzato nell'intestazione.
Motti,

I dati statici non sono migliori dei globi di ambito dei namespace.
coppro,

@coppro. Sono almeno un passo avanti nella catena evolutiva da globuli casuali in quanto possono essere resi privati ​​(ma altrimenti d'accordo).
Martin York,

@Motti: OTOH, se lo vuoi nell'intestazione (funzioni inline / template), sei tornato ad essere brutto al riguardo.
Shog9

1
Esempio interessante, usare una classe come scorciatoia per evitare di ripetere templateargomenti!
underscore_d

13

Dovresti usare uno spazio dei nomi, perché uno spazio dei nomi ha molti vantaggi rispetto a una classe:

  • Non è necessario definire tutto nella stessa intestazione
  • Non è necessario esporre tutta l'implementazione nell'intestazione
  • Non puoi usingun membro della classe; puoi usingun membro dello spazio dei nomi
  • Non puoi using class, anche se using namespacespesso non è una buona idea
  • L'uso di una classe implica che c'è qualche oggetto da creare quando non ce n'è davvero nessuno

I membri statici sono, a mio avviso, molto abusati. Non sono una vera necessità nella maggior parte dei casi. Le funzioni dei membri statici sono probabilmente migliori come funzioni dell'ambito dei file e i membri dei dati statici sono solo oggetti globali con una reputazione migliore e immeritata.


3
"Non è necessario esporre tutta l'implementazione nell'intestazione", nemmeno quando si utilizza una classe.
Vanuan,

Ancora di più: se usi gli spazi dei nomi non puoi esporre tutta l'implementazione nell'intestazione (finirai con una definizione multipla di simboli). Le funzioni integrate dei membri della classe ti consentono di farlo.
Vanuan,

1
@Vanuan: è possibile esporre le implementazioni dello spazio dei nomi nell'intestazione. Basta usare la inlineparola chiave per soddisfare ODR.
Thomas Eding,

@ThomasEding not need! =
Can

3
@Vanuan: solo una cosa è garantita dal compilatore durante l'utilizzo inline, e NON "allinea" il corpo di una funzione. Lo scopo reale (e garantito dallo standard) inlineè quello di prevenire definizioni multiple. Leggi "Una regola di definizione" per C ++. Inoltre, la domanda SO collegata non veniva compilata a causa di problemi di intestazione precompilati anziché problemi di ODR.
Thomas Eding,

3

Preferirei gli spazi dei nomi, in questo modo puoi avere dati privati ​​in uno spazio dei nomi anonimo nel file di implementazione (quindi non è necessario mostrarli nell'intestazione rispetto ai privatemembri). Un altro vantaggio è che dal usingtuo spazio dei nomi i clienti dei metodi possono scegliere di non specificareMyMath::


2
Puoi avere dati privati ​​in uno spazio dei nomi anonimo anche nel file di implementazione con le classi. Non sono sicuro di seguire la tua logica.
Patrick Johnmeyer,

0

Un motivo in più per utilizzare la classe: opzione per utilizzare gli identificatori di accesso. È quindi possibile suddividere il metodo statico pubblico in metodi privati ​​più piccoli. Il metodo pubblico può chiamare più metodi privati.


6
I modificatori di accesso sono fantastici, ma anche la maggior parte dei privatemetodi è più accessibile di un metodo il cui prototipo non è affatto pubblicato in un'intestazione (e quindi rimane invisibile). Non sto nemmeno menzionando l'incapsulamento migliore offerto da funzioni spaziate in modo anonimo.
Paercebal,

2
I metodi privati ​​sono, IMO, inferiori a nascondere la funzione stessa nell'implementazione (file cpp) e non esporla mai nel file di intestazione. Per favore approfondisci questo nella tua risposta e perché preferiresti usare membri privati . Fino ad allora -1.
nonsensickle

@nonsensickle Forse intende che una funzione mammut con molte sezioni ripetute può essere suddivisa in modo sicuro nascondendo le sottosezioni offensive dietro il privato, impedendo ad altri di raggiungerle se sono pericolose / hanno bisogno di un uso molto attento.
Troyseph,

1
@Troyseph anche così, puoi nascondere queste informazioni all'interno di uno spazio dei nomi senza nome nel .cppfile che lo renderebbe privato a quell'unità di traduzione senza fornire alcuna informazione superflua a chiunque leggesse il file di intestazione. In effetti, sto cercando di sostenere il linguaggio di PIMPL.
insensibile il

Non è possibile inserirlo in un .cppfile se si desidera utilizzare i modelli.
Yay295

0

Sia il namespace che il metodo class hanno i loro usi. Lo spazio dei nomi ha la possibilità di essere distribuito tra i file, tuttavia questo è un punto debole se è necessario imporre tutto il codice correlato per andare in un unico file. Come accennato in precedenza, la classe consente anche di creare membri statici privati ​​nella classe. Puoi averlo nello spazio dei nomi anonimo del file di implementazione, tuttavia è ancora un ambito più ampio rispetto ad averli all'interno della classe.


"[la memorizzazione di cose] nello spazio dei nomi anonimo del file di implementazione [è] un ambito più ampio rispetto al fatto di averli all'interno della classe" - no, non lo è. nei casi in cui non è richiesto l'accesso privilegiato ai membri, i contenuti in modo anonimo sono più privati di private:quelli. e in molti casi in cui sembra essere richiesto un accesso privilegiato , questo può essere preso in considerazione. la funzione più "privata" è quella che non appare in un'intestazione. private:i metodi non possono mai godere di questo vantaggio.
underscore_d
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.