2010-07-22 22 views
14

Sono sempre stato insegnato che i tipi non primitivi dovrebbero essere passati per riferimento const piuttosto che per valore, se possibile, vale a dire:Le piccole strutture semplici devono essere passate per riferimento const?

void foo(std::string str);//bad 
void foo(const std::string &str);//good 

Ma stavo pensando oggi che forse in realtà alcuni semplici tipi definiti dall'utente possono realmente essere meglio passati per valore es:

class Vector2 
{ 
public: 
    float x, y; 
    ...constructors, operators overloads, utility methods, etc... 
}; 

void foo(Vector2 pos); 
void foo(const Vector2 &pos);//is this really better than by value? 
void foo(float x, float y);//after all isn't by value effectively doing this? 

mio pensiero è che facendo passare il Vector2 per riferimento, è in realtà più costoso di passaggio per valore dal compilatore sta utilizzando un puntatore e dereferencing per accedere alla const Vector2 & pos versione?

È questo il caso? Gli oggetti semplici sono meglio passati per valore? Dove dovrebbe essere tracciata la linea?

+0

Si noti che se il proprio tipo di classe ha costruttori dichiarati dall'utente, non è POD. –

+6

La regola generale: passare per riferimento costante, a meno che 'sizeof (T) <= sizeof (void *)' o 'T' sia primitivo. – GManNickG

+2

@GMan, mi hai battuto (e +1). Anche se, generalizzerei è probabilmente inferiore o uguale alla dimensione della parola del processore. E, uno dovrebbe controllare le specifiche del processore per scoprire la dimensione ottimale. Se non sbaglio, i compilatori potrebbero potenzialmente ottimizzare un const-ref ad un passaggio per valore in alcuni casi, indipendentemente dalle dimensioni. Immagino che questo sia particolarmente vero in C++ 0x data la nuova semantica del movimento. –

risposta

7

Sì, gli oggetti semplici devono essere passati per valore. La linea deve essere disegnata secondo l'architettura. In caso di dubbio, profilo.

+0

+1 per la profilazione - è l'unico modo per dire quale è meglio che i tipi di POD –

1

Il passaggio con riferimento const evita la costruzione di un nuovo oggetto. Se il tuo costruttore non è banale, ad esempio, alloca la memoria, sarai molto meglio passare per riferimento const rispetto per valore.

Nel mondo C#, si prende questa decisione scegliendo se fare qualcosa di una classe o di una struttura. Le classi usano la semantica di riferimento mentre le strutture usano la semantica del valore. Tutto ciò che ho letto dice che dovresti generalmente scegliere di trasformare tutto in classe, a meno che non sia molto piccolo, cioè dell'ordine di 16 byte. Se stai cercando un cut-off per quando passare per valore rispetto a const riferimento in C++, 16 byte sembra una soglia ragionevole.

+1

non abbiano costruttori di copia non banali (o anche costruttori o distruttori di default non banali). –

+2

L'OP non ha detto esplicitamente il tipo POD. Ha detto ** semplice ** tipo definito dall'utente e poi ha dato un esempio che indicava che aveva un costruttore. –

0

Il mio pensiero è che facendo passare il Vector2 per riferimento, in realtà è più costoso di passaggio per valore dal momento che il compilatore è ora utilizzando un puntatore e dereferenziazione per accedere al const Vector2 & pos versione

Sarebbe vero se hai passato un oggetto dove size_of(object) < size_of(pointer_to_object). Ad esempio, char const& potrebbe essere più costoso di char

+2

Può anche essere vero se il dereferenziamento ripetuto comporterà un sovraccarico maggiore di quello che viene salvato non passando più dati. O se il riferimento impedisce ottimizzazioni a causa di problemi di aliasing. O se il riferimento ferisce la tua località. –

1

Proprio come una questione di politica passo ogni oggetto che non è un tipo di dati C di base di riferimento const. È molto più facile da ricordare in questo modo.

1

Un'analisi precedente, ma chiara, degli ins-n-out dei parametri di passaggio può essere trovata a http://www.ddj.com/184403855. Ovviamente C++ 0x ovvia a molti di questi problemi con la semantica del movimento, ma il documento fornisce molte giustificazioni sul perché spostare la semantica è auspicabile.

1

Un fattore che non ho visto menzionato è ciò che la routine sta per fare con il valore passato. A meno che la routine non sia espansa in linea, la manipolazione dei dati che vengono passati per riferimento richiederà più codice rispetto alla manipolazione dei dati che vengono passati per valore. Se i campi della struttura passata saranno accessibili in media meno di una volta ciascuno, questo overhead aggiuntivo sarà ridotto rispetto al sovraccarico di copia della struttura. Se saranno accessibili in media più volte ciascuno, probabilmente sarebbe meglio averli in pila. Se l'interfaccia è già impostata come pass-by-reference per una struttura a cui si accede pesantemente, potrebbe essere logico che la routine chiamata copi tutte le parti che sono di interesse. D'altra parte, se la routine chiamata sta per copiare molto o tutta una struttura in ogni caso, potrebbe anche essere passata per valore.

Problemi correlati