2013-08-02 10 views
8

To nie jest pytanie o to, dlaczego napisałbyś taki kod, ale bardziej jako pytanie o to, jak metoda jest wykonywana względem obiektu, z którym jest powiązana.Co się stanie, jeśli obiekt zmieni rozmiar własnego kontenera?

Jeśli mam struct takich jak:

struct F 
{ 
    // some member variables 
    void doSomething(std::vector<F>& vec) 
    { 
     // do some stuff 
     vec.push_back(F()); 
     // do some more stuff 
    } 
} 

i używam go tak:

std::vector<F>(10) vec; 
vec[0].doSomething(vec); 

Co się stanie jeśli push_back(...) w doSomething(...) powoduje, że wektor rozwinąć? Oznacza to, że vec[0] zostanie skopiowany, a następnie usunięty w trakcie wykonywania swojej metody. To nie byłoby dobre.

Czy ktoś mógłby wyjaśnić, co dokładnie tutaj się dzieje?

  • Czy program natychmiast się zawiesił? Czy metoda po prostu próbuje działać na danych, które nie istnieją?
  • Czy metoda działa "osierocona" obiektu, dopóki nie pojawi się problem, jak zmiana stanu obiektu?

Jestem zainteresowany tym, jak wywołanie metody jest powiązane z powiązanym obiektem.

Odpowiedz

7

Tak, jest źle. Możliwe, że Twój obiekt zostanie skopiowany (lub przeniesiony w C++ 11, jeśli rozróżnienie jest istotne dla twojego kodu), podczas gdy jesteś w środku doSomething(). Tak więc po tym, jak funkcja push_back() zwróci, wskaźnik ten nie będzie już wskazywał położenia twojego obiektu. Dla konkretnego przypadku wektora :: push_back(), możliwe jest, że pamięć wskazywana przez to została zwolniona, a dane skopiowane do nowej tablicy gdzie indziej. W przypadku innych pojemników (na przykład listy), które pozostawiają swoje elementy na miejscu, prawdopodobnie (prawdopodobnie) nie spowoduje to żadnych problemów.

W praktyce mało prawdopodobne jest natychmiastowe zawieszenie kodu. Najbardziej prawdopodobną okolicznością jest zapis na wolną pamięć i ciche zepsucie stanu twojego obiektu F. Możesz użyć narzędzi takich jak valgrind, aby wykryć tego rodzaju zachowanie.

Ale zasadniczo masz dobry pomysł: nie rób tego, to nie jest bezpieczne.

+0

Dzięki za odpowiedź. Czy mówisz, że program prawdopodobnie będzie kontynuowany bez awarii? Jeśli napiszę poza granicami tablicy do wolnej pamięci, zostanie to wykryte. Co pozwoliłoby na to, aby metoda mogła bez problemu zapisywać się w wolnej pamięci? – user487100

+2

Możesz praktycznie zawsze pisać do pamięci "uwolnionej" bez awarii. W ten sposób działa mapowanie pamięci. Mapa pozostaje w miejscu do wykorzystania przez przyszłe obiekty. Możesz ** nigdy ** pisać do wolnej pamięci "bez problemu". Tyle tylko, że "problemy" to coś innego niż upaść. –

+0

@ user487100: Jeśli napiszesz poza granicami tablicy do wolnej pamięci, to jest _sometimes_ detected. Łatwo jest stworzyć przypadek, w którym będę mógł napisać koniec i nie wykryć go przez jakiś czas. Przydzielenie 'char * a = new char [1]', a następnie odczyt i zapis do 'a [1]', prawdopodobnie nie spowoduje żadnych błędów na większości systemów. –

3

Czy ktoś mógłby wyjaśnić, co dokładnie tutaj się dzieje?

Tak. Jeśli dostęp do obiektu po push_back, resize lub insert została przydzielona jego zawartość vector „s, to niezdefiniowane zachowanie, czyli co faktycznie dzieje się do kompilatora, systemu operacyjnego, co do some more stuff jest i może szeregu inne czynniki, jak może faza księżyca, wilgotność powietrza w jakiejś odległej lokalizacji, ... jak to nazwać ;-)

W skrócie, jest to (pośrednio poprzez implementację std::vector) wywołanie destruktora samego obiektu, więc czas życia obiektu się skończył. Ponadto, pamięć poprzednio zajmowana przez obiekt została zwolniona przez przydział vector. Dlatego użycie niestatycznych elementów obiektu powoduje niezdefiniowane zachowanie, ponieważ wskaźnik przeniesiony do funkcji nie wskazuje już obiektu.Możesz jednak uzyskać dostęp do statycznych elementów klasy lub wywołać je:

struct F 
{ 
    static int i; 
    static int foo(); 

    double d; 
    void bar(); 

    // some member variables 
    void doSomething(std::vector<F>& vec) 
    { 
    vec.push_back(F()); 

    int n = foo(); //OK 
    i += n;  //OK 

    std::cout << d << '\n'; //UB - will most likely crash with access violation 
    bar();     //UB - what actually happens depends on the 
          //  implementation of bar 
    } 
} 
Powiązane problemy