2009-06-15 15 views
9

Così stavo scrivendo del codice, e ho avuto qualcosa di simile:Esiste un modo corretto per restituire una nuova istanza di oggetto mediante riferimento in C++?

class Box 
{ 
    private: 
    float x, y, w, h; 

    public: 
    //... 
    Rectangle & GetRect(void) const 
    { 
     return Rectangle(x, y, w, h); 
    } 
}; 

Poi più avanti nel codice:

Rectangle rect = theBox.GetRect(); 

che ha lavorato nella mia generazione di debug, ma in versione c'erano "problemi "restituendo quel rettangolo per riferimento - ho fondamentalmente un rettangolo non inizializzato. La classe Rectangle ha un operatore an = e un costruttore di copie. Senza entrare nel motivo per cui si è rotto, sono in realtà più interessato al modo corretto di restituire un oggetto (nuovo) tramite il riferimento allo scopo di assegnando la copia a una variabile. Sono solo sciocco? Non dovrebbe essere fatto? So che posso restituire un puntatore e quindi effettuare il dereferimento sull'assegnazione, ma preferirei di no. Una parte di me ha l'impressione che il ritorno di valore si traduca in una copia ridondante dell'oggetto: il compilatore lo calcola e lo ottimizza?

Sembra una domanda banale. Mi sento quasi in imbarazzo Non lo so dopo molti anni di codifica in C++ quindi spero che qualcuno possa chiarirlo. :)

+0

Si noti che non v'è alcun incarico nel codice. –

risposta

21

Non è possibile restituire un riferimento a un oggetto temporaneo nello stack. Sono disponibili tre opzioni:

  1. restituirlo per valore
  2. ritorno con riferimento tramite un puntatore a qualcosa che si è creato sul mucchio con l'operatore new.
  3. Restituisci per riferimento ciò che hai ricevuto come riferimento come argomento. [EDIT: Grazie a @ harshath.jr per la precisazione]

Si noti che quando si torna dal valore come nel seguente codice, il compilatore dovrebbe ottimizzare l'assegnazione al fine di evitare la copia - vale a dire sarà solo creare un single Rectangle (rect) ottimizzando la funzione create + assign + copy in una create. Funziona solo quando si crea il nuovo oggetto al ritorno dalla funzione.

Rectangle GetRect(void) const 
{ 
    return Rectangle(x, y, w, h); 
} 

Rectangle rect = theBox.GetRect(); 
+5

Ancora una volta, non vi è alcun incarico in questo codice. –

+3

Opzione 3: restituisci per riferimento ciò che hai ricevuto come argomentazione per riferimento.Questo è particolarmente utile durante l'override degli operatori. – jrharshath

+0

esempi di puntatore sarebbe stato bene in questa risposta come Rettangolo * getRect (void) const { tornare new Rectangle (x, y, w, h) } Rectangle * rect = theBox.GetRect() ; successivamente eliminare rect; – AppDeveloper

15

No, non puoi farlo. In sostanza, ciò che stai cercando di fare in questo esempio è restituire un riferimento a una variabile temporanea nello stack. Nel momento in cui viene restituito il riferimento, la variabile a cui punta verrà distrutta e quindi il riferimento non è valido.

+0

Quindi stai dicendo che se fosse const Rectangle & GetRect (...) sarebbe corretto? –

+0

Il problema non è la costanza, ma piuttosto la cosa a cui si ha un riferimento viene creata nello stack. –

+0

@pbhogan, no. Non avrei dovuto aggiungere la parola const alla mia risposta. È stato un errore da parte mia. Dovresti solo restituirlo per valore qui. – JaredPar

2
  • restituire un riferimento alle interiora della vostra classe Box (avere un membro Rectangle. Tornando è consigliato un riferimento const).
  • o semplicemente restituire un Rectangle. Si noti che l'utilizzo dell'idioma return SomeClass(a,b,c); attiverà probabilmente un return value optimization (RVO) sul compilatore decente.

Controllare l'implementazione std::complex per i dettagli.

2

Potresti sentirti confuso dal concetto di durata di un temporaneo. Considerate:

void f1(const A & a) { 
} 

A f2() { 
    return A; 
} 

f1(f2()); 

Questo è il codice OK, e lo standard dice che il senza nome temporanea che crea f2 deve appendere intorno abbastanza a lungo per essere utilizzabile in F1.

Tuttavia, il tuo caso è un po 'diverso. La cosa restituita dalla funzione è un riferimento e pertanto anche il temporaneo senza nome è un riferimento. Quel riferimento deve rimanere sospeso abbastanza a lungo da essere utile, ma la cosa a cui si riferisce non ha bisogno.

+0

Ovviamente hai ragione. Ho passato troppo tempo in linguaggi dinamici: ho iniziato a prevedere la vita del temporaneo per seguire il riferimento. –

1

Questo non è possibile. Un riferimento è un'altra forma di puntatore e in effetti si restituisce un indirizzo di un oggetto che sarà stato distrutto (chiamato dal distruttore) ed eventualmente sovrascritto dal momento in cui il chiamante riceve il controllo.

è possibile

  • chiamata nuovo e restituire un puntatore (forse si dovrebbe pensare a un puntatore intelligente) a un oggetto heap allocata o
  • ritorno per valore o
  • passare l'oggetto da riferimento in una funzione in modo che lo riempia.
7

La restituzione di un oggetto in base al valore (vedere l'esempio di seguito) può effettivamente essere meno costosa di quanto si pensi. Il compilatore spesso ottimizza la copia extra. Questo è chiamato il return value optimization.

Rectangle GetRect(void) const 
    { 
      return Rectangle(x, y, w, h); 
    } 
+0

+1 per la denominazione dell'ottimizzazione consentita specifica –

0

Se il bit a bit rettangolo appare come la scatola cioè si compone di quattro carri (ma ha diverse funzioni membro) è possibile utilizzare un reinterpret_cast, anche se non mi affatto consiglio:

const Rectangle & GetRect(void) const 
    { 
      assert(sizeof(Rectangle) == sizeof(Box)); 
      return reinterpret_cast <Rectangle> (*this); 
    } 
0

possiamo usare auto_ptr, se vogliamo usare nuovi e al sicuro da perdita di memoria

class Box { 

    private: float x, y, w, h; 

    public:  

    //...  

    std::auto_ptr<Rectangle> GetRect(void) const 
    {   
     return std::auto_ptr<Rectangle> (new Rectangle(x, y, w, h)); 
    } 

}; 
+0

al giorno d'oggi usa shared_ptr. – gbjbaanb

2

Esiste un modo corretto per restituire una nuova istanza di oggetto per riferimento in C++?

No, non per riferimento. Ci sono due modi per creare un nuovo oggetto:

della pila:

Rectangle makeRect() 
{ 
    return Rectangle(x, y, w, h); 
} 
Rectangle r = makeRect(); // return by value 

sul mucchio:

Rectangle * makeRect() 
{ 
    return new Rectangle(x, y, w, y); 
} 
Rectangle * r = makeRect(); // returned a pointer, don't forget to delete it later 

Perché non qualcosa di simile?

class Box 
{ 
    private: 
    Rectangle mRectangle; 

    public: 
    Box(float x, float y, float w, float h) : 
     mRectangle(x, y, w, h) // Forgive me for making assumptions 
          // about the inner workings of your 
          // code here. 
    { 
    } 

    const Rectangle & GetRect() const 
    { 
     return mRectangle; 
    } 
}; 

Rectangle rect = theBox.GetRect(); 

Il "compito" dovrebbe funzionare ora. (Tecnicamente questo non è un operatore di assegnazione, ma un costruttore di copia viene richiamato.)

Sperando di aiutare

+0

Un buon riassunto delle mie opzioni, grazie. Applico la tua ultima opzione ogni volta che è possibile, tuttavia il mio esempio qui è stato piuttosto semplicistico. Box è in realtà qualcosa di più complicato e GetRect restituisce un rettangolo calcolato. Ad ogni modo, la tua prima opzione è quello che dovrei fare ... Stavo solo cercando di essere più intelligente del compilatore, immagino: p –

Problemi correlati