2012-02-17 13 views
13

Wydaje się, że za każdym razem, gdy dodaję obiekt do wektora m_test, wywoływana jest metoda destruktora. Czy czegoś brakuje? Jak mogę temu zapobiec?Dlaczego destruktor mojej klasy jest wywoływany podczas dodawania instancji do wektora?

class TEST 
{ 
public: 
    TEST(); 
    ~TEST(); 
    int * x; 
}; 

TEST::TEST() 
{ 
} 

TEST::~TEST() 
{ 
... it is called every time I push_back something to the vector ... 
    delete x; 
} 

    vector<TEST> m_test; 
    for (unsigned int i=0; i<5; i++) 
    { 
     m_test.push_back(TEST()); 
    } 
+2

W C++ 11 można użyć 'm_test.emplace_back()', aby uniknąć tworzenia tymczasowego. W każdym razie zawsze pamiętaj o [Regule Trzech] (http://stackoverflow.com/questions/4172722). –

Odpowiedz

9

Problem polega na tym, że jesteś naruszenie Rule of Three. Twoja klasa ma destruktor, więc potrzebujesz również konstruktora kopiowania i operatora przypisania. Ewentualnie nie można zezwolić na kopiowanie klasy (na przykład przez uczynienie T(T const&) i T& operator=(T const&) prywatnej lub przez uzyskanie z boost::noncopyable), a następnie zmianę rozmiaru wektora zamiast użycia push_back.

W pierwszym przypadku możesz po prostu push_back w swojej klasie, tak jak zwykle.W drugim, składnia byłoby coś

std::vector<TEST> vec(5); 
// vec now has five default-constructed elements of type TEST. 

Nie robi żadnej z tych rzeczy jest to zły pomysł, ponieważ jest bardzo prawdopodobne, aby uruchomić w kwestii podwójnych delecją w pewnym momencie - nawet jeśli uważasz, że” Nigdy nie kopiuj ani nie przypisz TEST gdzie x != nullptr, o wiele bezpieczniej jest jawnie to zabronić.

Nawiasem mówiąc, jeśli masz wskazówki członkowskich, które powinny zostać usunięte, gdy obiekt znajdzie się poza zakresem, należy rozważyć użycie inteligentnych wskazówek jak scoped_ptr, unique_ptr i shared_ptr (a może auto_ptr jeśli jesteś w stanie wykorzystać podwyższenie lub C + +11).

+0

Dziękuję Anton, jestem nowy w programowaniu C++ i nigdy nie słyszałem o Regule Trzech. Dziękuję za wskazanie mi właściwego kierunku. – 2607

7

To nie nazywa kiedy push_back, nazywa gdy tymczasowy jest niszczony.

Aby rozwiązać go w przykładzie:

TEST test; 
for (int i = 0; i < 5; ++i) 
{ 
    m_test.push_back(test); 
} 

powinien wywołać tylko raz.

Twój kod tworzy tymczasowe TEST w pętli, używając go w push_back, to tymczasowe wyjście wykracza poza zakres, kiedy pętla się kończy/powtarza i zostaje zniszczona. To się dzieje dokładnie tak, jak powinno, ponieważ tymczasowe potrzeby muszą zostać oczyszczone.

Jeśli chcesz tego uniknąć, musisz zrobić cokolwiek innego, ale zrobić tymczasowy obiekt dla każdego naciśnięcia. Jednym z potencjalnych rozwiązań jest:

vector<TEST> m_test(5); // Note reserving space in the vector for 5 objects 

std::fill(m_test.begin(), m_test.end(), TEST()); // Fill the vector with the default ctor 

W zależności od tego, jak optymalizuje się STL, może nie być konieczne wykonanie wielu kopii.

może także być w stanie uzyskać lepsze prowadzenie jeśli wdrożenie konstruktor kopiujący w swojej klasie TEST, jak:

TEST::TEST(const TEST & other) 
{ 
    x = new int(*other.x); // Not entirely safe, but the simplest copy ctor for this example. 
} 

Czy jest to właściwe, lub jak go obsługiwać, zależy od klasy i jej potrzeb, ale zazwyczaj powinieneś mieć konstruktora kopiowania, gdy zdefiniujesz swój własny zwykły konstruktor i destruktor (w przeciwnym wypadku kompilator wygeneruje jeden, iw tym przypadku spowoduje to skopiowanie i zwinięcie wskaźników do x).

1

vector.push_back() kopiuje dany obiekt do jego obszaru przechowywania. Tymczasowy obiekt, który tworzysz w wywołaniu push_back(), jest niszczony natychmiast po skopiowaniu i to właśnie widzisz. Niektóre kompilatory mogą zoptymalizować tę kopię, ale najwyraźniej nie.

1

W m_test.push_back(TEST());, TEST() utworzy zmienną tymczasową. Po skopiowaniu wektora do własnej pamięci zmienna tymczasowa zostaje zniszczona.

Możesz zrobić tak:

vector<TEST> m_test(5, TEST()); 
0

Aby uniknąć zniszczenia tymczasowego obiektu i, aby uniknąć konstruktorów kopiowania, należy rozważyć użycie vector::resize lub vector::emplace_back. Oto przykład z użyciem emplace_back:

vector<TEST> m_test; 
m_test.reserve(5); 
for (uint i=0; i<5; i++) 
{ 
    m_test.emplace_back(); 
} 

Element wektor zostanie zbudowana na miejscu, bez konieczności kopiowania. Gdy vt zostanie zniszczony, każdy element wektorowy zostanie automatycznie zniszczony.

Wymagane jest C++ 0x (użyj -std=c++0x z gnu). #include <vector> jest oczywiście również wymagane.

Jeśli domyślny konstruktor nie jest używany (na przykład, jeśli TEST::x było odniesienie zamiast wskaźnika), wystarczy dodać arguements na wezwanie do emplace_back() następująco:

class TEST 
{ 
public: 
    TEST(int & arg) : x(arg) {;} // no default constructor 
    int & x; // reference instead of a pointer. 
}; 

. . . 

int someInt; 

vector<TEST> m_test; 
m_test.reserve(5); 
for (uint i=0; i<5; i++) { 
    m_test.emplace_back(someInt); // TEST constructor args added here. 
} 

reserve() pokazany jest opcjonalny ale zapewnia, że ​​dostępna jest wystarczająca ilość miejsca przed rozpoczęciem konstruowania elementów wektorowych.

Powiązane problemy