reference - Come "restituire un oggetto" in C++?




performance return (7)

Non voglio restituire un valore copiato perché è inefficiente

Provalo.

Cerca RVO e NRVO e in semantica del movimento C ++ 0x. Nella maggior parte dei casi in C ++ 03, un parametro out è solo un buon modo per rendere il tuo codice brutto, e in C ++ 0x ti farai davvero male usando un parametro out.

Basta scrivere codice pulito, restituire per valore. Se le prestazioni sono un problema, profilalo (smetti di indovinare) e trova ciò che puoi fare per risolverlo. Probabilmente non restituirà le cose dalle funzioni.

Detto questo, se sei decisa a scrivere in questo modo, probabilmente vorrai fare il parametro out. Evita l'allocazione dinamica della memoria, che è più sicura e generalmente più veloce. Richiede che tu abbia un modo per costruire l'oggetto prima di chiamare la funzione, il che non ha sempre senso per tutti gli oggetti.

Se si desidera utilizzare l'allocazione dinamica, il minimo che si può fare è metterlo in un puntatore intelligente. (Questo dovrebbe essere fatto in ogni caso) Quindi non ti preoccupare di cancellare nulla, le cose sono eccezionalmente sicure, ecc. L'unico problema è che probabilmente è più lento del valore restituito comunque!

So che il titolo sembra familiare perché ci sono molte domande simili, ma sto chiedendo un aspetto diverso del problema (conosco la differenza tra avere cose in pila e metterle in pila).

In Java posso sempre restituire riferimenti ad oggetti "locali"

public Thing calculateThing() {
    Thing thing = new Thing();
    // do calculations and modify thing
    return thing;
}

In C ++, per fare qualcosa di simile ho 2 opzioni

(1) Posso usare i riferimenti ogni volta che ho bisogno di "restituire" un oggetto

void calculateThing(Thing& thing) {
    // do calculations and modify thing
}

Quindi usalo in questo modo

Thing thing;
calculateThing(thing);

(2) O posso restituire un puntatore ad un oggetto allocato dinamicamente

Thing* calculateThing() {
    Thing* thing(new Thing());
    // do calculations and modify thing
    return thing;
}

Quindi usalo in questo modo

Thing* thing = calculateThing();
delete thing;

Usando il primo approccio non dovrò liberare la memoria manualmente, ma per me rende il codice difficile da leggere. Il problema con il secondo approccio è che dovrò ricordare di delete thing; , che non sembra molto carino. Non voglio restituire un valore copiato perché è inefficiente (credo), quindi ecco che arrivano le domande

  • Esiste una terza soluzione (che non richiede la copia del valore)?
  • C'è qualche problema se mi atteno alla prima soluzione?
  • Quando e perché dovrei usare la seconda soluzione?

Sono sicuro che un esperto di C ++ avrà una risposta migliore, ma personalmente mi piace il secondo approccio. L'uso di puntatori intelligenti aiuta a dimenticare l' delete e, come dici tu, sembra più pulito di dover creare un oggetto in anticipo (e doverlo eliminare ancora se lo si vuole allocare nell'heap).


Un modo rapido per determinare se viene chiamato un costruttore di copie è aggiungere la registrazione al costruttore della copia della classe:

MyClass::MyClass(const MyClass &other)
{
    std::cout << "Copy constructor was called" << std::endl;
}

MyClass someFunction()
{
    MyClass dummy;
    return dummy;
}

Chiama someFunction ; il numero di "Costruttore copia è stato chiamato" le linee che otterrete variano tra 0, 1 e 2. Se non ne ottieni, il compilatore ha ottimizzato il valore di ritorno (che è permesso fare). Se ottieni non ottieni 0, e il tuo costruttore di copie è ridicolmente costoso, quindi cerca modi alternativi per restituire istanze dalle tue funzioni.


Per prima cosa hai un errore nel codice, vuoi dire Thing *thing(new Thing()); e return thing; solo return thing; .

  • Usa shared_ptr<Thing> . Deref come se fosse un puntatore. Verrà cancellato per te quando l'ultimo riferimento alla Thing contenuta non rientra nello scope.
  • La prima soluzione è molto comune nelle librerie naive. Ha delle prestazioni e un overhead sintattico, evitarlo se possibile
  • Utilizzare la seconda soluzione solo se è possibile garantire che non verranno generate eccezioni o quando le prestazioni sono assolutamente critiche (si interagirà con C o assembly prima che ciò diventi rilevante).

Basta creare l'oggetto e restituirlo

Thing calculateThing() {
    Thing thing;
    // do calculations and modify thing
     return thing;
}

Penso che ti farai un favore se ti dimentichi dell'ottimizzazione e scrivi solo codice leggibile (dovrai eseguire un profilatore più tardi, ma non pre-ottimizzare).


Basta restituire un oggetto come questo:

Thing calculateThing() 
{
   Thing thing();
   // do calculations and modify thing
   return thing;
}

Questo invocherà il costruttore di copia su Things, quindi potresti voler eseguire la tua implementazione. Come questo:

Thing(const Thing& aThing) {}

Questo potrebbe rallentare un po ', ma potrebbe non essere affatto un problema.

Aggiornare

Il compilatore probabilmente ottimizzerà la chiamata al costruttore di copie, quindi non ci sarà alcun overhead aggiuntivo. (Come ha sottolineato Dreamlax nel commento).


Un riferimento non può mai essere NULL .







c++ reference performance return