2016-05-01 16 views
11

Poniższy kodCzy obiekt może sam się wymazać ze standardowego kontenera C++?

#include <iostream> 
#include <map> 

struct foo 
{ 
    void kill(std::map<int, foo>& m, int i) 
    { 
    m.erase(i); 
    } 
}; 

int main() 
{ 
    std::map<int, foo> m; 

    m.emplace(1, foo()); 

    std::cout << m.size() << std::endl; 

    m[1].kill(m, 1); 

    std::cout << m.size() << std::endl; 
} 

kompiluje bez ostrzeżenia (g ++), wykonuje bezbłędnie i sądząc po wyjściu metoda kill kasuje foo obiekt z mapy. Uważam jednak, że może to być nieokreślone zachowanie. Wydaje się, że metoda m.erase(i) po linii m.erase(i) nie wskazuje już na poprawny obiekt.

Co mówi o tym standard C++?

+1

Tak, może. http://stackoverflow.com/questions/862093/object-delete-itself-from-container – Auriga

+1

Zasadniczo taka sama zasada jak ["usuń to;"] (http://stackoverflow.com/q/3150942/2069064) – Barry

Odpowiedz

7

Po wprowadzeniu kill, m[1] (od m[1].kill(m, 1);) oświadczenie zostało w pełni ocenione jako przedmiot foo dzwonisz kill dalej.

Następnie wykonuje się m.erase(i);, kończąc niszczenie bieżącego obiektu foo.

ile piszesz żadnego oświadczenia użyciu bieżącego obiektu (this) przed powrotem z funkcji kill, to całkowicie dopuszczalne i bezpieczne (jak komentował słupkami odwołuje Auriga i Barry). Nawet jeśli bieżący obiekt już nie istnieje, twoja funkcja powróci bezpiecznie ze stosu, nie ma powodu, by zawieść, o ile wiem.

Jako ilustracja, byłoby to skończyć z niezdefiniowanej zachowań i nie może być dokonana:

struct foo 
{ 
    void kill(std::map<int, foo>& m, int i) 
    { 
    m.erase(i); 
    cout << attribute; // don't do that! current foo object does not exist anymore 
    } 
    int attribute; 
}; 

Więc powiedzmy, że to co robisz jest ryzykowne, ale ważne i bezpieczne, jeśli robisz to dobrze.

Jako ilustrację, to byłoby skończyć z określonym zachowaniem i może odbywać się:

struct foo 
{ 
    void kill(std::map<int, foo>& m, int i) 
    { 
    int theAttribute = attribute; 
    m.erase(i); 
    cout << theAttribute; // OK! 
    } 
    int attribute; 
}; 

uwzględniając sposób usunąć bieżący obiekt prawdopodobnie nie jest to dobra praktyka, mimo to (szczególnie jeśli inny deweloper modyfikuje kod później ... mógł łatwo spowodować awarię z pierwszym przykładem powyżej). Przynajmniej umieścić wyraźny komentarz w kodzie powiedzieć bieżący obiekt mógł zostać zniszczony (zauważ, że kill może zniszczyć bieżący obiekt, drugi lub brak ... W zależności od treści i im):

struct foo 
{ 
    void kill(std::map<int, foo>& m, int i) 
    { 
    m.erase(i); 
    // careful! current object could have been destroyed by above statement and may not be valid anymore! Don't use it anymore! 
    } 
}; 
-4

To nie jest bezpieczne. m.erase(i) pozwoli uniknąć próby wymazania tego samego obiektu, jeśli zostanie wywołany wiele razy, ale m[1].kill(m, 1) jest niezdefiniowanym zachowaniem, jeśli zostanie wywołane więcej niż raz. Aby być nieco bezpieczniejszym, m.at(1).kill(m, 1) spowoduje błąd out_of_range.

+0

moja odpowiedź jest zła, czy ludzie są po prostu szczęśliwi z DV dzisiaj? – user6279021

+0

@StoryTeller Niezdefiniowane zachowanie jest niezdefiniowanym zachowaniem. Pokazuje to, że 'm [1]' nie jest już poprawne. – user6279021

+2

Nie jest to nawet niezdefiniowane zachowanie, jeśli zostanie wywołane więcej niż raz ... 'm [1]' najpierw wstawi nowy obiekt, który zostanie natychmiast usunięty. – Barry

Powiązane problemy