2009-06-15 12 views
9

Więc pisałem jakiś kod, a ja miałem coś takiego:Czy istnieje właściwy sposób na zwrócenie nowej instancji obiektu przez odwołanie w C++?

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

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

potem w jakiś kod:

Rectangle rect = theBox.GetRect(); 

który działał w moim debugowania, ale w wydaniu były „problemy "zwracanie tego prostokąta przez odniesienie - w zasadzie otrzymałem niezainicjowany prostokąt. Klasa Rectangle ma operator = i konstruktor kopii. Nie wdając się w to, dlaczego to się zepsuło, bardziej interesuje mnie poprawny sposób zwrócenia (nowego) obiektu przez odniesienie dla celów kopiowania do zmiennej . Czy jestem po prostu głupi? Czy nie powinno to być zrobione? Wiem, że mogę zwrócić wskaźnik, a następnie usunąć dereferencję z zadania, ale wolałbym nie. Jakaś część mnie czuje, że powracanie przez wartość skutkowałoby redundantnym kopiowaniem obiektu - czy kompilator je wymyślił i zoptymalizował?

Wydaje się, że to banalne pytanie. Czuję się prawie zakłopotany, nie wiem o tym po wielu latach kodowania w C++, więc mam nadzieję, że ktoś może to dla mnie wyjaśnić. :)

+0

pamiętać, że istnieje żadne przyporządkowanie w kodzie. –

Odpowiedz

21

Nie można zwrócić odniesienia do obiektu tymczasowego na stosie. Do wyboru są trzy opcje:

  1. odesłać go przez wartość
  2. Powrót przez odniesienie poprzez wskaźnik do czegoś, który został utworzony na stercie z nowym operatorem.
  3. Powracaj przez referencję, co otrzymałeś jako referencję jako argument. [EDIT: Dzięki @ harshath.jr za wskazanie tego]

Zwróć uwagę, że po powrocie przez wartość, jak w poniższym kodzie, kompilator powinien zoptymalizować przypisanie, aby uniknąć kopiowania - to znaczy po prostu utworzyć pojedynczy prostokąt (rect), optymalizując tworzenie + przypisz + kopiuj do stworzenia. Działa to tylko wtedy, gdy tworzysz nowy obiekt po powrocie z funkcji.

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

Rectangle rect = theBox.GetRect(); 
+5

Po raz kolejny nie ma przypisania w tym kodzie. –

+3

Opcja 3: wróć przez odniesienie, co otrzymałeś jako argument przez odniesienie.Jest to szczególnie przydatne podczas przesuwania operatorów. – jrharshath

+0

przykłady wskaźnik będzie już dobrze w tej odpowiedzi jak prostokąt * getRect (void) const { powrotu nowy prostokąt (X, Y, W, H) } prostokątne * rect = theBox.GetRect() ; później delete rect; – AppDeveloper

15

Nie możesz tego zrobić. Zasadniczo to, co próbujesz zrobić w tym przykładzie, zwraca referencję do zmiennej tymczasowej na stosie. Zanim odniesienie zostanie zwrócone, zmienna, na którą wskazuje, zostanie zniszczona, a więc odniesienie jest nieważne.

+0

Więc mówisz, że jeśli był to Rectangle & GetRect (...), to byłoby poprawne? –

+0

Problem nie jest constness, ale raczej, że rzecz, do której masz odniesienia, jest tworzony na stosie. –

+0

@pbhogan, nie. Nie powinienem był dodawać słowa const do mojej odpowiedzi. To był błąd z mojej strony. Powinieneś po prostu zwrócić tę wartość według wartości tutaj. – JaredPar

2
  • Albo wrócić odniesienie do wnętrzności swojej klasie Box (posiada człon Rectangle. Zwracanie const odniesienia jest zalecane).
  • lub po prostu zwróć Rectangle. Zauważ, że użycie idiomu return SomeClass(a,b,c); prawdopodobnie wyzwoli return value optimization (RVO) na przyzwoitym kompilatorze.

Sprawdź szczegóły implementacji std::complex.

2

Być może dezorientujesz się pojęciem czasu życia tymczasowego. Rozważmy:

void f1(const A & a) { 
} 

A f2() { 
    return A; 
} 

f1(f2()); 

Jest to kod OK, a średnia mówi, że bezimienny tymczasowy że F2 tworzy okrągły musi powiesić na tyle długo, aby być użyteczny w F1.

Jednak twoja sprawa jest nieco inna. To, co zwraca twoja funkcja, jest odniesieniem, a zatem bezimienny czasowy jest również odniesieniem. Odwołanie to musi być wystarczająco długie, aby było użyteczne, ale to, czego dotyczy, nie musi.

+0

Masz oczywiście rację. Spędzałem zbyt wiele czasu w dynamicznych językach - zacząłem oczekiwać, że czas życia tymczasowego będzie zgodny z referencją. –

1

To nie jest możliwe. Odwołanie jest inną formą wskaźnika, a ty zwracasz adres obiektu, który zostanie zniszczony (wywołany destruktor), a być może nawet nadpisany przez czas, w którym dzwoniący otrzymuje kontrolę.

Można

  • wezwanie nowe i zwraca wskaźnik (może powinieneś pomyśleć o inteligentnych wskaźnik) do obiektu sterty przydzielone lub
  • powrót przez wartość lub
  • przekazać obiekt przez odwołanie do funkcji, więc wypełnia ją.
7

Zwrócenie obiektu według wartości (patrz przykład poniżej) może w rzeczywistości być tańsze niż myślisz. Kompilator często optymalizuje dodatkową kopię. Nazywa się to return value optimization.

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

+1 za nazwę konkretnej dozwolonej optymalizacji –

0

Jeśli bitowe prostokąt wygląda Box czyli składa się z czterech pływaków (ale mam różne funkcje członków) można użyć reinterpret_cast, choć nie byłoby w ogóle go polecam:

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

możemy użyć auto_ptr, jeśli chcemy wykorzystać nowe i bezpieczne od Przeciek pamięci

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

użyj shared_ptr nowadays. – gbjbaanb

2

Czy istnieje właściwy sposób na zwrócenie nowej instancji obiektu przez odwołanie w C++?

Nie, nie przez odniesienie. Istnieją dwa sposoby, aby utworzyć nowy obiekt:

na stosie:

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

na stercie:

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

Dlaczego nie coś takiego?

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(); 

"Zadania" powinny teraz działać. (Technicznie nie jest to operator przypisania, ale konstruktor kopia jest wywoływany.)

Licząc na pomoc

+0

Dobre podsumowanie moich opcji, dziękuję. W miarę możliwości zaimplementuję Twoją ostatnią opcję, jednak mój przykład tutaj był nieco uproszczony. Box jest w rzeczywistości czymś bardziej skomplikowanym, a GetRect zwraca obliczony prostokąt. W każdym razie pierwszą opcją jest to, co powinienem robić ... Próbowałem być mądrzejszy od kompilatora: p –