2009-06-05 7 views
8

Diciamo che ho un oggetto Employee_Storage che contiene un membro di dati di connessione al database. Questo membro dei dati dovrebbe essere memorizzato come puntatore o come riferimento?Quando è preferibile memorizzare i membri dei dati come riferimenti anziché puntatori?

  • Se devo conservare come riferimento, mi non hanno a che fare qualsiasi NULL controllo. (Quanto importante è NULL controllando comunque?)

  • Se devo conservare come un puntatore, è più facile da configurare Employee_Storage (o MockEmployee_Storage) per le fini del test.

In genere, avevo l'abitudine di memorizzare sempre i miei dati come riferimenti. Tuttavia, questo rende i miei oggetti finti difficili da configurare, perché invece di essere in grado di passare in NULL s (presumibilmente all'interno di un costruttore predefinito) ora devo passare in oggetti true/mock.

Esiste una buona regola generale da seguire, specificatamente per quanto riguarda la verificabilità?

risposta

10

È preferibile memorizzare i riferimenti come membri dei dati se vengono assegnati alla costruzione e non c'è davvero alcun motivo per cambiarli. Poiché i riferimenti non possono essere riassegnati, sono molto limitati.

In generale, di solito viene memorizzato come puntatore (o una qualche forma di puntatore intelligente basato su modelli). Questo è molto più flessibile - sia per i test (come hai detto), ma anche solo in termini di utilizzo normale.

+0

Per quanto riguarda il tuo punto sull'utilizzo di riferimenti solo quando non c'è motivo di cambiarli mai - Non potrei realizzare la stessa cosa con i puntatori usando un puntatore const? (Oggetto * const m_ptr <- Questo puntatore non può essere modificato.) Se è così, sembra che ci sia ancora meno motivo per usare i riferimenti ... – Runcible

+0

@Runcible: True, anche se l'uso dei riferimenti è un po ' "più bello" rispetto all'uso dei membri dei puntatori, quindi se si vuole veramente questo comportamento, i riferimenti sono spesso più puliti con cui lavorare. Uso molto raramente i riferimenti per i membri, tuttavia - nella maggior parte dei casi è troppo poco flessibile. –

+0

Vedo. E che ne pensi di NULL? Dato che stai usando i membri del puntatore, devi fare il controllo NULL dappertutto? Mi è stato detto che troppi controlli NULL equivalgono solo alla programmazione paranoica e al codice aggiuntivo. Sono curioso di sapere qual è il tuo approccio. – Runcible

7

Non è quasi mai preferibile memorizzare i riferimenti come membri dei dati e il più delle volte è impossibile. Se gli oggetti devono essere assegnabili (poiché devono essere memorizzati in un contenitore di libreria standard), i riferimenti non possono essere utilizzati. Inoltre, i riferimenti non possono essere ripristinati, quindi una volta che un riferimento è inizializzato con un oggetto, non può essere fatto riferirsi a un oggetto diverso.

Vedere questa domanda Should I prefer pointers or references in member data? per una discussione più dettagliata del problema.

2

Data una scelta, mi piace usare il tipo più limitato possibile. Quindi, se non ho bisogno di sostenere oggetti nulli avrei preferiscono di gran lunga a dichiarare un membro

Foo& m_foo; 

piuttosto che un membro

Foo*const m_foo; 

, perché la precedente dichiarazione documenta il fatto che m_foo può essere nullo A breve termine, il vantaggio non è eccezionale. Ma nel lungo periodo, quando si ritorna al vecchio codice, l'immediata certezza che non ci si deve preoccupare del caso in cui lo m_foo sia nullo è abbastanza prezioso.

Esistono altri modi per ottenere un effetto simile. Un progetto su cui ho lavorato in cui non comprendevano i riferimenti avrebbe insistito sul fatto che qualsiasi indicatore potenzialmente nullo fosse suffisso '00', ad esempio m_foo00. È interessante notare che boost::optional sembra support references anche se non l'ho provato. Oppure puoi sparpagliare il tuo codice con delle affermazioni.

3

Stavo cercando di capirlo da solo, quindi potrei anche pubblicarlo. Concludo che non sembra essere una buona idea usare un membro dei dati di riferimento perché potresti inavvertitamente creare un alias quando inizi a inizializzarlo.

#include <iostream> 
using namespace std; 
class stuff 
{ 
public: 
explicit stuff(int &a):x(a) //you have to initialize it here 
{ 
//body intialization won't work 
}; 
int& x; //reference data member 
}; 

int main() 
{ 
int A=100; 
stuff B(A);//intialize B.x 
cout<<B.x<<endl;//outputs 100 
A=50;//change A; 
cout<<B.x<<endl; //outputs 50, so B.x is an alias of A. 
system("pause"); 
return 0; 
} 
+0

sarebbe lo stesso se lo si passa come puntatore a quella variabile locale A. – Tebe