2014-09-25 10 views
5

Jestem obecnie czytając ten poradnik/wyjaśnienie referencji rvalue:Czy wyrażenie "nowa T" określa wartość r lub wartość l?

http://thbecker.net/articles/rvalue_references/section_07.html

W 2 do ostatniego akapitu, autor wspomina, że ​​"argument konstruktora kopii T w ciele fabryki jest lwartość ". Kod on ma na myśli to:

template<typename T, typename Arg> 
shared_ptr<T> factory(Arg const & arg) 
{ 
    return shared_ptr<T>(new T(arg)); 
} 

Zdaję sobie sprawę, że new T(arg) konstruuje obiekt T na stercie, ale nie jest zwracana wartość tymczasowa wartość wskaźnika które zostaną utracone, jeśli nie jest używany (co prowadzi do wyciek pamięci, jak sądzę), a zatem wartość r?

EDYCJA: Aby wyjaśnić, rozumiem, że nie będzie wyciek pamięci w tym przykładzie. Chodzi mi o to, że jeśli wartość wskaźnika nie byłaby użyta, nie mielibyśmy dostępu do skonstruowanego obiektu T, a tym samym do wycieku pamięci.

+0

['std :: shared_ptr '] (http://en.cppreference.com/w/cpp/memory/shared_ptr) skutecznie zapewnia __lwartośc_. –

+0

Plik shared_ptr zostanie wyczyszczony, jeśli nie jest już do niego odwoływany. To znaczy. jeśli zwrócisz metodę shared_ptr z metody i nie jest ona używana w kodzie wywołującym, metoda shared_ptr zostanie automatycznie wyczyszczona. – cageman

+0

Myślę, że 'new' zwraca wartość' rvalue', która jest kopiowana do wewnętrznej wartości 'l std :: shared_ptr'. – Galik

Odpowiedz

3

Krótka odpowiedź, jestem pewien, że ktoś napisze już jeden, ale:

new daje wskaźnik rvalue: new int = nullptr; zawiedzie skompilować z błędem około wymagające lwartość.

dereferencing że wskaźnik daje lwartość: *(new int) = 5; zostanie skompilowany (to naiwne stwierdzenie przecieka również pamięć, bo wskaźnik jest stracone, oczywiście).

Konstruktor kopiowania przyjmuje odwołanie, więc jeśli masz wskaźnik do obiektu, musisz go usunąć.

Jeśli zgubisz wskaźnik, nie możesz go usunąć, więc nie zostanie zniszczony, a pamięć sterty nie zostanie zwolniona (dopóki program nie wyjdzie i pamięć zostanie zwrócona do systemu operacyjnego).

Po umieszczeniu wskaźnika na innym obiekcie, który może przejąć jego własność, np. shared_ptr, nie zgubisz wskaźnika. Drugi obiekt usunie go zgodnie z jego semantyką, najpóźniej wtedy, gdy drugi obiekt (lub ostatni, w przypadku współwłasności, podobnie jak z shared_ptr) zostanie zniszczony.

1

Zwrot z new to nie prvalue lwartością, ponieważ nie można napisać:

new T(arg) = ....; // not legal, so no lvalue 

Standard definiuje (§3.10): lwartością (tzw historycznie, ponieważ lwartościami mogłyby pojawić się na po lewej stronie wyrażenia przypisania) oznacza funkcję lub obiekt. A zwrot z new oznacza adres obiektu, a nie sam obiekt.

Jeśli przypisać tę wartość do wskaźnika a i dereference tego wskaźnika, a nawet jeśli by dereference nowej wartości zwracanej bezpośrednio zrobić to lwartością:

*new T(arg) = ....; // valid, althoug you'd loose the address ! 
*a = ....;  // normal poitner dereferencing 

Jest to wyjaśnione w normie (§ 5.3.1/1): Jednoargumentowy operator * wykonuje niedokładność: wyrażenie, do którego zostanie zastosowane, jest wskaźnikiem do typu obiektu lub wskaźnikiem do typu funkcji, a wynik jest lwartością odnoszącą się do obiektu lub funkcji, do której punkty ekspresji.

Ważne: edycja i shared_ptr

Twój przykład nie przecieka: shared_ptr<T>(new T(arg)) tworzy wspólny wskaźnik, który dba o nowo utworzonego obiektu i usunie go, jeśli nie jest używany.

Dwie możliwości:

  • powrót wspólny wskaźnik jest używany w wyrażeniu (na przykład jako tymczasowy udostępnionego wskaźnik lub przydzielony do innego obiektu shared_ptr): obiekt jest kopiowany, a jego liczba stosowanie jest zaktualizowana odzwierciedla liczbę żywych odniesień do niego. Bez wycieku!

  • Wspólny wskaźnik retuned jest ignorowany: zwracany obiekt jest niszczony, jego liczba użytków jest zmniejszana, a ponieważ nie jest używana gdzie indziej, obiekt jest usuwany.

+1

Co dowodzi ten przykład przydziału? Deklaruj 'const int i = 5'. Nie możesz napisać 'i = ...'. Jednak "i" jest lwartością. Zadeklaruj 'void foo()'. Nie możesz napisać 'foo = ...'. Jednak 'foo' jest lwartością. – AnT

+0

Tak, zgadzam się, że zasada kciuka nie jest w 100% dokładna w tym sensie, że nie bierze pod uwagę niemodyfikowanych obiektów (const i funkcji). – Christophe

+1

Cóż, funkcja wcale nie jest obiektem. Pojęcie "lwartości" zostało oderwane od anegdotycznego testu "można użyć po lewej stronie zadania" dawno temu. O wiele dokładniejszym testem, wywodzącym się z samej definicji wartości l, jest test przez wbudowany jednoargumentowy operator '&'. Ale nawet ten test nie jest w 100% dokładny. – AnT

1

Niestety kategoria wartość operatorów i ich argumentami są underspecified który pozostawia nas w niefortunnym położeniu, aby wydedukować kategorię wartości. Wielu zakłada, że ​​jeśli nie zostanie to wyraźnie określone, wynikiem jest prura.

Możemy zrobić całkiem zgrabną domysły, próbując użyć new po lewej stronie, która miała stronę zadania i zobacz, że to nie działa i dochodzimy do wniosku, że nie daje ona l-wartości, ale nadal pozostajemy w nieokreślonym terytorium.

Możemy określić go jako empirycznie tego code from Luc Danton answer here does:

template<typename T> 
struct value_category { 
    // Or can be an integral or enum value 
    static constexpr auto value = "prvalue"; 
}; 

template<typename T> 
struct value_category<T&> { 
    static constexpr auto value = "lvalue"; 
}; 

template<typename T> 
struct value_category<T&&> { 
    static constexpr auto value = "xvalue"; 
}; 

// Double parens for ensuring we inspect an expression, 
// not an entity 
#define VALUE_CATEGORY(expr) value_category<decltype((expr))>::value 

i wynik:

std::cout << VALUE_CATEGORY(new int) << std::endl ; 

jest:

prvalue

, który potwierdza nasz wniosek za pomocą dedukcji.

+0

Poza tym, że analizuje tylko konkretną implementację C++ – Yakk

+0

@Yakk, spędziłem trochę czasu na zapoznanie się z tym, kiedy zadałem [to pytanie] (http://stackoverflow.com/q/21053273/1708801) i wydaje się, że chociaż jest to oficjalnie underspecified w ten sposób [komentarz Josepha Mansfielda] (http://stackoverflow.com/questions/14991219/what-jest-na-najdniej-kategorii-pomocy-operatora-co-operatorów-nieokreślonego?lq= 1 # comment21055383_14991297) iz innych źródeł wydaje się, że twórcy kompilatorów zgodzili się na te rzeczy, więc myślę, że jest mało prawdopodobne, że będzie się różnić, choć technicznie mogli. –

+0

Można również znaleźć w [takich raportach o wadach] (http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#637), że komitet wyraźnie ma jasny obraz wartości kategorie tych wyrażeń nie są oficjalnie określone w standardzie. Mam nadzieję, że [raport wady Josepha] (http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1642) ostatecznie doprowadzi do tego, że zostanie to bardziej skodyfikowane w standardzie, jak [użycie nieokreślonych wartości było w C++ 14] (http://stackoverflow.com/q/21319413/1708801). –

2

Wielu współpracowników wydaje się nie wiedzieć, jakie są kategorie wartości, niektórzy posuwają się nawet do sugerowania, że ​​shared_ptr "przechowuje lwartość", co nie ma żadnego sensu.

L-wartości i wartościowość tak naprawdę nie mają nic wspólnego z tym, co coś "wraca" lub z stanem obiektów w pamięci; chodzi o stan wyrażeń w kodzie. To, czy wyrażenie jest wartością l, czy wartością rownową (lub jedną z pozostałych), pochodzi z różnych reguł i konstrukcji językowych.

W skrócie, wyrażenia lwartości to nazwy, a wszystko inne nie jest wyrażeniem lwartości. Jednym godnym uwagi rodzajem wyjątku od tej reguły jest *ptr, gdzie ptr jest typu wskaźnika, ponieważ zdefiniowano *ptr, aby uzyskać wartość l ze względów historycznych (chyba, że ​​dotyczy to przeciążenia operatora).

Teraz nie jest wyraźnie zasugerowane w standardzie, że new "zwraca" (ocenia) wartość, ponieważ to stwierdzenie nie ma żadnego sensu. new oznacza wskaźnik do nowego bloku pamięci, a zgodnie z regułami języka wyrażenie new T jest wartością rwartalną, ponieważ to właśnie oznacza wartość rna.

Trudno to wyjaśnić lepiej bez pisania książki, ale to jest sedno tego.

Zdaję sobie sprawę, że nowy T (arg) konstruuje obiekt T na stercie, ale nie jest zwracana wartość tymczasowa wartość wskaźnika które zostaną utracone, jeśli nie jest używany (co prowadzi do wycieku pamięci chyba), a zatem runek?

Tak, zasadniczo. Fakt, że jest wskaźnikiem dynamicznie przydzielanej pamięci, jest zupełnie nieistotny.

+0

Zmieniono tytuł, aby odzwierciedlić to, co właśnie powiedziałeś. Mam nadzieję, że teraz ma więcej sensu. Dzięki. – elatalhm

+0

@elatalhm: To lepiej! –

Powiązane problemy