make_unique i unique.release() a wyciek pamięci.
Ostatnio zmodyfikowano 2017-10-02 15:25
0x07 Temat założony przez niniejszego użytkownika |
make_unique i unique.release() a wyciek pamięci. » 2017-10-02 11:36:28 Witam zacnych Użytkowników tego forum. Zastanawia mnie kwestia zarządzania pamięcią w przypadku użycia uniqe_ptr.release() jako wartości zwracanej przez funkcję członkowską klasy. Myślę, że najlepiej zobrazować to na przykładzie prostego kodu (kod zaczerpnięty z https://en.wikibooks.org/wiki/C%2B%2B_Programming/Code/Design_Patterns): class Pizza { public: void setDough( const string & dough ) { m_dough = dough; } void setSauce( const string & sauce ) { m_sauce = sauce; } void setTopping( const string & topping ) { m_topping = topping; } void open() const { cout << "Pizza with " << m_dough << " dough, " << m_sauce << " sauce and " << m_topping << " topping. Mmm." << endl; } private: string m_dough; string m_sauce; string m_topping; };
class PizzaBuilder { public: virtual ~PizzaBuilder() { } Pizza * GetPizza() { return m_pizza.release(); } void CreateNewPizzaProduct() { m_pizza = make_unique < Pizza >(); } virtual void buildDough() = 0; virtual void buildSauce() = 0; virtual void buildTopping() = 0; protected: unique_ptr < Pizza > m_pizza; };
class Cook { public: void openPizza() { m_pizzaBuilder->GetPizza()->open(); } void MakePizza( PizzaBuilder * pb ) { m_pizzaBuilder = pb; m_pizzaBuilder->CreateNewPizzaProduct(); m_pizzaBuilder->buildDough(); m_pizzaBuilder->buildSauce(); m_pizzaBuilder->buildTopping(); } private: PizzaBuilder * m_pizzaBuilder; };
Klasa Cook jako argument metody MakePizza przyjmuje adres obiektu abstrakcyjnej klasy bazowej lub pochodnych. Następnie wywołuje metodę CreateNewPizzaProduct, która inicjalizuje wskaźnik uniqe_ptr domyślnie zainicjalizowanym obiektem Pizza po czym inicjalizuje jego składowe prywatne przy pomocy metod klas pochodnych klasy PizzaBuilder. Wywołanie metody openPizza klasy Cook prowadzi do wywołania metody GetPizza klasy PizzaBuilider. W metodzie tej wskaźnik unique_ptr jest pozbawiany własności przy pomocy release(), zaś sam obiekt Pizza jest przypisywany do tymczasowego surowego wskaźnika Pizza*, który służy do wywołania funkcji open klasy Pizza. Moje wątpliwości dotyczą zagadnienia wycieku pamięci, ponieważ z tego co mi wiadomo obiekt "odebrany" wskaźnikowi unique_ptr należy przypisać do innego wskaźnika, aby później móc go zniszczyć. Tutaj jednak po zakończeniu działania metody openPizza, wskaźnik Pizza* posiadający obiekt Pizza jest automatycznie zwalniany. Czy w ten sposób odzyskiwana jest pamięć po obiekcie Pizza? Przyznam szczerze, że inteligentne wskaźniki są dla mnie tematem nowym i nie w pełni oczywistym. Czy jest ktoś tak uprzejmy, aby wytłumaczyć mi, w którym miejscu dochodzi (lub nie dochodzi) do zwolnienia pamięci? A może moje rozumowanie jest błędne w zupełnie innym miejscu; jeśli tak, to w którym. Dziękuję każdemu cierpliwemu Użytkownikowi za okazaną pomoc. Pozdrawiam serdecznie 0x07 |
|
Monika90 |
» 2017-10-02 13:19:05 Kod jest wbrew zasadom i nie zwalnia pamięci. Funkcja GetPizza powinna zwracać unique_ptr i już będzie lepiej. |
|
0x07 Temat założony przez niniejszego użytkownika |
» 2017-10-02 14:03:42 Powinienem do tego użyć std::move czy raczej utworzyć lokalny unique_ptr i zainicjalizować go przy pomocy release()? Czy fragment kodu poniżej jest już prawidłowy? Czy pomiędzy obiema metodami istnieje jakaś wyraźna różnica (np. dla wydajności przy częstszym stosowaniu obydwu składni), czy to po prostu kwestia gustu? Można to napisać jeszcze inaczej? std::unique_ptr < Pizza > GetPizza() { return std::move( m_pizza ); }
unique_ptr < Pizza > GetPizza() { unique_ptr < Pizza > pz( m_pizza.release() ); return pz; }
W ten sposób wskaźnik m_pizza zostanie usunięty automatycznie wraz z końcem życia obiektu PizzaBuilder, a przekazany obiekt zostanie usunięty wraz z zakończeniem życia zwracanego przez funkcję unique_ptr. Dobrze rozumiem? PS Wiem, że zadaję bardzo dużo pytań, ale usprawiedliwiam to niepohamowanym apetytem na wiedzę z zakresu C++ (wiem, wiem, muszę uważać żeby się nie "przejeść"). Z kolei w sieci bardzo trudno jest natrafić na zadowalającą wypowiedź na temat bardzo skonkretyzowanego pytania. @Edit: Chyba już wszystko rozumiem. Dzięki @Monika90 za dodatkową odpowiedź poniżej. Temat wyczerpany, zamykam. |
|
Monika90 |
» 2017-10-02 15:25:23 Obydwa sposoby robią to samo, ale ten pierwszy jest lepszy bo kod jest krótszy i wprost wyraża intencje programisty. Ten drugi wygląda jak napisany przez kogoś, kto nie wiedział że istnieje std::move. |
|
« 1 » |