2009-09-07 8 views
5

Zrobiłem następujące trochę Program: (w zasadzie klasa Couts jeśli zostanie utworzony, kopiowane lub zniszczone, a głównym że robi trochę tego)std C++ zniszczenie elementu pojemnik i zachowanie wstawiania

class Foo 
{ 
public: 
Foo(string name): _name(name) 
{ 
    cout << "Instance " << _name << " of Foo created!" << std::endl; 
}; 
Foo(const Foo& other): _name(other._name) 
{ 
    cout << "Instance " << _name << " of Foo copied!" << std::endl; 
}; 

~Foo() 
{ 
    cout << "Instance " << _name << " of Foo destroyed!" << std::endl; 
} 
string _name; 
}; 



int main(int argc, char**argv) 
{ 
Foo albert("Albert"); 
Foo bert("Bert"); 
{ 
    vector<Foo> v1, v2; 
    system("PAUSE"); 

    v1.push_back(albert); 
    system("PAUSE"); 

    v2.push_back(bert); 
    system("PAUSE"); 

    v1 = v2; 
    system("PAUSE"); 
} 
    system("PAUSE"); 
} 

wyjście wygląda następująco:

Instance Albert of class Foo created! 
Instance Bert of class Foo created! 
Press any key... 
Instance Albert of class Foo copied!  
Instance Albert of class Foo copied!  // why another copy? 
Instance Albert of class Foo destroyed! // and destruction? 
Press any key... 
Instance Bert of class Foo copied! 
Instance Bert of class Foo copied! 
Instance Bert of class Foo destroyed! 
Press any key...      // v1=v2 why did the albert instance not get destroyed? 
Press any key...      
Instance Bert of class A destroyed! 
Instance Bert of class A destroyed! 
Press any key...      // there's still an albert living in the void 

to wydaje mi się bardzo dziwne. Dlaczego nawet kłopoczę się przekazaniem czegoś jako odnośnikiem, jeśli mimo to zostanie on skopiowany dwukrotnie? Dlaczego v1.operator = (inne) nie niszczy zawartych w nim elementów? Dobrze pasuje do zachowania shared_ptr. Czy ktoś może mi powiedzieć, dlaczego?

ADDITION kładę to w nieskończonej pętli i sprawdził wykorzystanie mem, nie wydaje się, aby produkować przeciek mem przynajmniej.

ADDITION OK, mem nie stanowi problemu, ponieważ używa operatora = zamiast ctorowania, ok, dziękuję. Kiedy dodać

v1.reserve(10); 
v2.reserve(10); 

numer logiczny kopii odbywa. bez tego ponownie przydziela i kopiuje cały wektor dla każdego push_back (co jest dość opóźnione nawet dla małych wektorów). Patrząc na to będę rozważyć użycie .reserve coraz zoptymalizować operatorów przypisania jak diabli :)

Ponadto: PODSUMOWANIE

  1. Wszystkie te kwestie wydają specyficzne dla VC++ 2005.
  2. Jeśli rozmiar dwóch kontenerów jest taki sam, moja implementacja używa operatora = na elementach zamiast niszczyć stare i kopiować nowe, co wydaje się być praktyką dźwiękową . JEŚLI rozmiary różnią się, stosuje się normalne zniszczenie i kopiowanie.
  3. Wraz z wdrożeniem z 2005 r. Należy użyć rezerwy! W przeciwnym razie fatalna, a nie stabilna wydajność.
  4. Te czarne skrzynki są o wiele czarniejsze niż myślałem.
+0

Czy próbowałeś skompilować to jako kompilację wydania? – jalf

+0

Tak. Ten sam wynik. – AndreasT

+0

Łatwo jest się wypróbować, po prostu skopiuj wklej do pustego projektu i dodaj elementy programu iostream, wektor i ciąg znaków, i gotowe. – AndreasT

Odpowiedz

4

Dlaczego nawet kłopotałem się przekazaniem czegoś jako odniesieniem, jeśli mimo to zostanie ono skopiowane dwukrotnie?

Typy kontenerów STL należy traktować jak blackbox, które mogą kopiować przechowywane obiekty tak często, jak są potrzebne. Na przykład za każdym razem, gdy rozmiar kontenera zostanie zmieniony, wszystkie obiekty zostaną skopiowane.

Jest możliwe, że implementacja twojego kompilatora push_back() używa tymczasowej dodatkowej kopii. Na moim komputerze (gcc na Mac OS X) nie ma żadnych dodatkowych kopii podczas push_back() (w zależności od wyjścia twojego programu).

Ta kopia dzieje się gdzieś w kodzie STL, a nie w konstruktorze kopiowania (ponieważ używa odwołania).

Dlaczego v1.operator = (inne) nie niszczy zawartych w nim elementów?

Foo::operator= zostanie wywołana dla "Albert" przykład z "bert" przykład jako argument. Dlatego nie ma tu żadnej niejawnej operacji niszczenia i kopiowania. Może chcesz to sprawdzić poprzez zapewnienie własną implementację dla operatora:

Foo& operator=(const Foo& other) { 
    cout << "Instance " << other._name << " of Foo assigned to " << _name << "!" << std::endl; 
    return *this; 
} 

To daje następujące dane wyjściowe na moim komputerze:

Instance Albert z Foo stworzony!
Utworzono instancję Bert of Foo!
Instance Albert of Foo copied!
Instance Bert of Foo copied!
Instancja Bert of Foo przydzielona do Alberta!
Instancja Bert of Foo destroyed!
Instancja Alberta z Foo została zniszczona!
Instancja Bert of Foo destroyed!
Instancja Alberta z Foo została zniszczona!

+1

Należy pamiętać, że samo śledzenie kopii oznacza, że ​​Twój autor kopii staje się bardziej złożony do tego stopnia, że ​​optymalizator nie może go już wyeliminować. Więc liczą się ctorowane połączenia, które istnieją tylko dlatego, że są policzone! – MSalters

+0

Argument czarnego pola jest ważny, jednak zawsze miałem nadzieję, że to nie były głupie czarne skrzynki :). Cóż, kolejne rozczarowanie M $. – AndreasT

+0

@MSalters: To jest interesujące. Zasada nieoznaczoności C++. – stribika

3

Jest operator generowany automatycznie =. Gdy użyjesz v1 = v2, ten operator zostanie użyty. W tym momencie jedna z instancji "albert" staje się "bert". Spróbuj dodać tę funkcję do Foo:

Foo& operator = (const Foo& rval) { 
    cout << _name << " = " << rval._name << endl; 
    _name = rval._name; 
    return *this; 
} 

To jest taka sama jak auto generowane ale wypisuje komunikat diagnostyczny, dzięki czemu można zobaczyć, co się dzieje.

+0

cóż, a co z kopiami nadmiarowymi i wyciekiem memów (co w rzeczywistości się nie dzieje) – AndreasT

+1

Po dodaniu tej funkcji (lub statycznego licznika) można zauważyć, że nie ma wycieku. Jeśli chodzi o dodatkowe kopie, nie ma gwarancji, że kontener STL skopiuje tylko raz. Może to być przekaz wartości lub coś innego w zależności od implementacji STL. – stribika

1

"Podwójne kopiowanie" nie ma miejsca po skompilowaniu z GCC. Musi to być specyficzne dla sposobu implementacji std :: vector w VC++.

+0

Wydaje się specyficzne dla VS2005 – AndreasT

1

Visual Studio 2008 daje mi następujący wynik:

Instance Albert of Foo created! 
Instance Bert of Foo created! 
Press any key to continue . . . 
Instance Albert of Foo copied! 
Press any key to continue . . . 
Instance Bert of Foo copied! 
Press any key to continue . . . 
Press any key to continue . . . << here auto-generated operator= doing its job 
Instance Bert of Foo destroyed! 
Instance Bert of Foo destroyed! << this is Albert was originally 
Press any key to continue . . .

wydaje się, że std::vector realizacja nie jest bardzo skuteczny w VS2005.

+1

To zdecydowanie prawda! – AndreasT