2013-05-03 16 views
11

AvereIs (* i) .Membro meno efficienti rispetto i-> membro

struct Person { 
    string name; 
}; 

Person* p = ... 

Si supponga che non gli operatori sono sovraccarichi.


Quale è più efficiente (se presente)?

(*p).namevs.p->name

Da qualche parte nella parte posteriore della mia testa ho sentito alcune campane, che l'operatore * dereferenziazione può creare una copia temporanea di un oggetto; è vero?


Lo sfondo di questa domanda sono casi come questo:

Person& Person::someFunction(){ 
    ... 
    return *this; 
} 

e ho cominciato a chiedermi, se cambia il risultato al Person* e l'ultima riga semplicemente return this avrebbe fatto alcuna differenza (in termini di prestazioni)?

+0

A questo punto dovresti preoccuparti molto della leggibilità, e dato che '->' è lo zucchero sintattico più comune, '(* i) .m' è disapprovato in molti posti. Solo se qualche profilo mostra che potrebbe esserci un problema relativo a questo, dovresti iniziare a preoccuparti della sua efficienza. – PlasmaHH

+3

Mi aspetto che il compilatore produca esattamente lo stesso risultato. Puoi rispondere da solo, compilare entrambi i codici per l'assemblatore e controllare il codice prodotto. – bluehallu

+0

Ho posto questa domanda a causa dello * sfondo * che ho descritto - molti operatori di base di solito restituiscono un riferimento a se stessi, e vedo molto spesso che restituiscono '* this'; –

risposta

8

Quando si restituisce un riferimento, equivale esattamente a passare un puntatore, semantica del puntatore esclusa.
Si restituisce un elemento sizeof(void*), non uno sizeof(yourClass).

Quindi, quando si farlo:

Person& Person::someFunction(){ 
    ... 
    return *this; 
} 

Si ritorna un riferimento, e che di riferimento ha la stessa dimensione intrinseca di un puntatore, quindi non c'è alcuna differenza di runtime.

Lo stesso vale per l'uso di (*i).name, ma in quel caso si crea un l-value, che ha poi la stessa semantica come riferimento (vedi anche here)

+0

Questo è un buon punto sulle dimensioni del reso. Puoi spiegare, cosa fa '* p' solo da solo allora? Il risultato è una persona e una persona, o una persona e? –

+0

Si ottiene un 'l-value', che fa riferimento all'indirizzo dell'oggetto, se leggo http://stackoverflow.com/questions/11347111/dereferencing-a-pointer-when-passing-by-reference correttamente e in base a wikipedia: http://en.wikipedia.org/wiki/Value_(computer_science) – Gui13

+0

Letture interessanti nei collegamenti, grazie :) –

9

Non c'è differenza. Anche lo standard dice che i due sono equivalenti, e se c'è qualche compilatore là fuori che non genera lo stesso binario per entrambe le versioni, è uno cattivo.

+0

Lo stesso per i tipi built-in e definiti dall'utente ?? –

+3

@KalamarObliwy supponendo che nessun operatore sovraccarichi, sì. –

+0

E per i puntatori intelligenti standard ('std :: unique_ptr',' std :: shared_ptr'), gli operatori sovraccaricati si comportano sempre allo stesso modo. – MSalters

1

Qualsiasi buon compilatore produrrà gli stessi risultati. Puoi rispondere da solo, compilare entrambi i codici per l'assemblatore e controllare il codice prodotto.

+2

https://ideone.com/Ovj7ao – BoBTFish

+0

@BoBTFish * voodoo * –

2

Sì, è molto più difficile da leggere e tipo, in modo da stai molto meglio usando x->y rispetto a (*x).y - ma a parte l'efficienza di digitazione, non c'è assolutamente alcuna differenza. Il compilatore ha ancora bisogno di leggere il valore di x e quindi aggiungere l'offset y, se si utilizza una forma o l'altra [assumendo non ci sono divertenti oggetti/classi coinvolte che hanno la priorità sulle operator-> e operator*, rispettivamente, ovviamente]

Non è stato creato alcun oggetto aggiuntivo quando si fa riferimento a (*x). Il valore del puntatore viene caricato in un registro nel processore [1]. Questo è tutto.

Il ritorno di un riferimento è in genere più efficiente, in quanto restituisce un puntatore (in incognito) all'oggetto, anziché creare una copia dell'oggetto. Per oggetti più grandi delle dimensioni di un puntatore, in genere si tratta di una vittoria.

[1] Sì, possiamo avere un compilatore C++ per un processore che non ha registri. Conosco almeno un processore di Rank-Xerox che ho visto nel 1984 circa, che non ha registri, era un processore LiSP dedicato, e ha solo uno stack per gli oggetti LiSP ... Ma sono lontani dal comune nel mondo di oggi. Se qualcuno sta lavorando su un processore che non dispone di registri, si prega di non svitare la mia risposta semplicemente perché non copro quell'opzione. Sto cercando di mantenere la risposta semplice.

+3

-1 per non considerare processori senza r ... oh, aspetta. :) –

Problemi correlati