2010-08-31 12 views
41

situazioni comuni:Quando passare per riferimento e quando passare da un puntatore in C++?

  1. Passando std :: string a una funzione foo (std :: string *) o foo (std :: string &);
  2. Passare tr1 :: shared_ptr a una funzione foo (tr1 :: shared_ptr * ptr) o foo (tr1 :: shared_ptr & ptr);

In generale, ciò che è una buona pratica. Mi confondo sempre. All'inizio, passare tutto come riferimenti sembra coerente, tuttavia non è possibile passare in Letterali come riferimenti o NULL come riferimenti.

Allo stesso modo, avere tutto come puntatori sembra buono, ma avendo poi devo preoccuparsi che i puntatori potrebbero essere puntare a NULL e controllare quelle condizioni all'inizio di tale funzione.

Pensi che il seguente frammento sia valido?

#include <iostream> 
#include <vector> 
#include <map> 
#include <string> 
#include <tr1/memory> 
#include <algorithm> 
using namespace std; 
using namespace std::tr1; 

int main(){ 
     map<string, shared_ptr<vector<string> > > adjacencyMap; 
     vector<string>* myFriends = new vector<string>(); 
     myFriends->push_back(string("a")); 
     myFriends->push_back(string("v")); 
     myFriends->push_back(string("g")); 
     adjacencyMap["s"] = shared_ptr<vector<string> >(myFriends); 
     return 0; 
} 

Grazie Ajay

+0

'foo (tr1 :: shared_ptr * ptr)' questo mi sembra molto sbagliato dal punto di vista del buon design a mio parere. sai dei riferimenti costanti? – Anycorn

+3

Perché non metti 'myFriends' in un puntatore intelligente subito? – GManNickG

+2

Penso che questo sia un duplicato di [Come passare oggetti alle funzioni in C++?] (Http: // StackOverflow.it/questions/2139224/how-to-pass-objects-to-functions-in-c), dove ho già risposto alla domanda generale. – sbi

risposta

23

I riferimenti sono più facili da ottenere.

Il tuo problema con i letterali è che non stai usando riferimenti const? Non è possibile associare un riferimento temporaneo (prodotto da un valore letterale) a un riferimento non const, perché non ha senso modificarne uno. È possibile associare uno a un riferimento const.

In particolare, quando si passa un argomento a una funzione e la funzione non lo cambierà e non è un tipo predefinito, passa per riferimento const. Funziona in modo analogo al passaggio per valore, tranne per il fatto che non richiede una chiamata al costruttore di copia.

I puntatori sono utili in quanto hanno un valore valido garantito che è possibile verificare. A volte questo è irrilevante, ea volte è molto importante. Ovviamente, non è generalmente possibile passare letteralmente un puntatore, a meno che (nel caso di una stringa letterale) lo sia già.

Alcuni standard di codifica dicono che non dovrebbe mai essere passato nulla tramite riferimento non const, poiché non fornisce alcuna indicazione sul punto di chiamata che l'argomento possa essere modificato dalla funzione. In tal caso, ti verrà richiesto di passare da un puntatore.Non sono favorevole a questo, in particolare perché gli strumenti di programmazione rendono più facile e più semplice ottenere la firma della funzione, in modo da poter vedere se una funzione potrebbe cambiare un argomento. Tuttavia, quando si lavora in un gruppo o per un'impresa, la coerenza dello stile è più importante di ogni singolo elemento di stile.

1

Io preferirei

map<string, shared_ptr<vector<string> > > adjacencyMap; 
    shared_ptr<vector<string> > myFriends(new vector<string>()); 
    myFriends->push_back(string("a")); 
    myFriends->push_back(string("v")); 
    myFriends->push_back(string("g")); 
    adjacencyMap["s"] = myFriends; 
    return 0; 

come questo assicura la vostra gestione var locale è sicura rispetto alle eccezioni.

Non vedo davvero come questo si riferisca alla vostra q che riguardava il merito di ref vs ptr, però. In entrambi gli esempi citati mi aspetterei di utilizzare la seconda forma (ref).

2

È possibile cercare http://www.cplusplus.com/forum/beginner/3958/ per alcuni approfondimenti. Utile anche: http://www.velocityreviews.com/forums/t284603-pointers-vs-references-a-question-on-style.html

Suppongo che non ci sia una risposta "giusta". È necessario valutare i pro e i contro di ciascun approccio tenendo conto del contesto specifico del progetto.

Personalmente preferisco i riferimenti, ma mi raccomando comunque di leggere quei post e musa su di esso.

2

Come regola generale, provare sempre a passare i parametri facendo riferimento a const. Passaggi puntatori possono portare a problemi di proprietà, nonché un sacco di altre possibilità di errori sottili.

Qual è lo scopo di NULL? Per indicare un puntatore/oggetto non valido. Se hai intenzione di passare oggetti non validi a una funzione, tutto ciò che devi fare è avere un metodo per verificare la validità di un oggetto. Come in:

void myfunc(const obj& myobj) 
{ 
    if(myobj.valid()) 
    // DO SOMETHING 
} 

tipi primitivi che ci si solito vuole passare per valore in ogni caso, dal momento che c'è così poca testa. E questo è il momento in cui useresti i letterali per lo più la maggior parte del tempo. Per le stringhe, dovresti provare a utilizzare std::string e stare lontano dalle stringhe in stile C const char* il più possibile. Ovviamente se devi usare stringhe di tipo C, non hai altra scelta che usare i puntatori, ma tutti i riferimenti dovrebbero essere la strada da percorrere.

Oh, e per essere veramente sicura rispetto alle eccezioni cercare di evitare questo:

vector<string>* myFriends = new vector<string>(); 
... 
adjacencyMap["s"] = shared_ptr<vector<string> >(myFriends); 

Invece fare:

shared_ptr<vector<string> > myFriends(new vector<string>()); 

Look in per RAII e l'eccezione di sicurezza per il motivo per cui questo è il metodo preferito.

3

Nel mio precedente lavoro, avevamo la regola che i semplici riferimenti non erano praticamente mai usati. Invece abbiamo deciso di:

  • passaggio per valore (per a buon mercato per copiare gli oggetti, tutte le primitive, i tipi di valore di piccole dimensioni, std :: string , archi molto piccoli o refcounted)
  • passaggio di const di riferimento (per accesso in sola lettura a oggetti di grandi dimensioni)
  • passaggio dal puntatore se avete bisogno di lettura e scrittura

Se ognuno segue queste regole, si può supporre che i parametri passati alle funzioni non vengono modificati a meno che il loro indirizzo è stata presa. Ha funzionato per noi.

+0

Perché passare std :: string per valore? Dovrebbe essere di riferimento const. Si dovrebbe anche passare per riferimento per impostazione predefinita anche quando si effettua l'accesso in lettura-scrittura. – ronag

+0

Cosa fare se si desidera modificare una primitiva all'interno della funzione? In che modo Point Point soddisfa questo caso? – user855

+0

+1, piuttosto ragionevole. @aja passa per puntatore (numero 3) @ron numero 2 dice così. "dovrebbe" è davvero una questione di preferenza – Anycorn

3

Probabilmente non è una risposta alla domanda. Bacia e basta.

int main() 
{ 
     multimap<string, string> adjacencyMap; 
     adjacencyMap.insert(std::make_pair("s", "a")); 
     adjacencyMap.insert(std::make_pair("s", "v")); 
     adjacencyMap.insert(std::make_pair("s", "g")); 
     return 0; 
} 
+0

Grazie! Sebbene questo non risponda alla mia domanda, non sapevo di multimap. E questo uso sembra molto appropriato per questo caso. – user855

+0

Da un lato, sembra che ci sia un colpo di memoria in questo approccio. Quando si utilizza un multimap, la stessa chiave viene memorizzata ripetutamente più volte. Nella mia appraoch, poiché le chiavi sono uniche, vengono memorizzate una sola volta e l'utilizzo dello spazio è inferiore. Qualche idea su questo? – user855

+0

Questa è l'implementazione specifica. Credo che la maggior parte delle implementazioni memorizzi solo una chiave una volta inserita. – ronag

22

Una buona regola empirica: "Usa i riferimenti quando puoi e i puntatori quando devi".

4

Io davvero non capisco perché è arrivata a tutti questi problemi:

std::map < std::string, std::vector<std::string> > adjacencyMap; 
std::vector<std::string>& sFriends = adjacencyMap["s"]; 
sFriends.push_back("a"); 
sFriends.push_back("v"); 
sFriends.push_back("g"); 

Perché si intromettersi shared_ptr qui? La situazione certamente non lo richiede!

Problemi correlati