Изтриване на дефинирани от потребителя вектори - C++

Имам 2 класа, да речем A & B. Клас B има собствен деструктор. В рамките на клас A имам вектор от указатели към обекти от клас B. Векторът е както следва:

vector<B*> vect;

В деструктора за клас A, как да извлека памет? Ако преминавам през вектора, извличам ли всеки обект и използвам ли delete за всеки извлечен обект? Пробвах това в деструктора, но се получава грешка.

Всяка помощ при решаването на този проблем е добре дошла. Съжалявам, но не мога да публикувам кода.


person Sriram    schedule 24.06.2010    source източник
comment
Преди да отговорим на този въпрос. Трябва да знаем как поставяте нещата във вектора (създадени ли са обектите с нови (това отваря друг въпрос кой притежава обектите, които току-що сте създали?)). Друг въпрос е защо поставяте указатели във вектор. Векторът е проектиран да поеме собствеността върху „обектите“, така че освен ако вашите B не са полиморфни, може да е по-добре да поставите B обекти (не указатели) във вектора.   -  person Martin York    schedule 25.06.2010


Отговори (6)


Някои други публикации посочиха, че е по-добре да използвате интелигентни указатели вместо указатели. Ако трябва да използвате указател по някаква причина, първо трябва да ги изтриете в цикъл.

for ( std::vector<B*>::iterator it = vect.begin(); it != vect.end(); ++it)
    delete (*it);
vect.clear();

редактиране: Ако вашата програма има грешка в сегмента в деструктора, тогава кодът ви е грешен. Може би сте поставили елемент на стека по адрес във вектора, но за да изтриете обект, той трябва да е в купчината.

#include <iostream>
#include <vector>
#include <string>

class data {
public:
    std::string d;
    data(std::string _d) : d(_d) { }
};

class container {
public:
    std::vector<data*> c;
    container() { c.clear(); }
    void add (data *d) { c.push_back(d); }
    ~container() {
        for (std::vector<data*>::iterator it = c.begin(); it != c.end(); ++it)
            delete (*it); 
        c.clear();
    }
};

int main (int argc, char* argv[]) {

    typedef std::vector<std::string> sVec;
    typedef sVec::iterator sVecIter;

    std::vector<std::string> cmd (argv+1, argv+argc);

    {
    container k;            
    for (sVecIter it = cmd.begin(); it != cmd.end(); ++it)
        k.add(new data((*it)));

    }

    return 0;

}

Това работи без проблем.

person DaClown    schedule 24.06.2010

Ако A притежава нещата, посочени от vect, тогава трябва да може да delete всеки елемент в рамките на vect. Ако се появи грешка, докато го правите, значи имате грешка някъде във вашия код.

Като цяло обаче е по-добре да използвате интелигентни указатели. ptr_vector на Boost (част от Boost.Pointer Container е предназначен за вашия конкретен пример, но просто std::vector<std::tr1::shared_ptr<B> > също ще работи (макар и с повече режийни разходи и по-неудобен синтаксис).

person Josh Kelley    schedule 24.06.2010

Да, ако елементите от тип B* сочат към обекти, разпределени в купчината, тогава за всеки елемент трябва да извикате delete върху него.

person Brian R. Bondy    schedule 24.06.2010

Да, ще завъртите вектора и ще изтриете всеки елемент. Проблемът е, че сте забравили хавлиения оператор на линия 42.

person Edward Strange    schedule 24.06.2010
comment
ха, „Проблемът е, че си забравил оператора на кърпите на линия 42.“ - person Greg Domjan; 25.06.2010
comment
Какво е това, забравихте оператора на хавлиите на линия 42? Някаква шега ли е? - person ; 25.06.2010
comment
@robin: Хубав справочник за стопаджии - който обаче бих предпочел да видя в коментар. - person Georg Fritzsche; 25.06.2010
comment
OP казва, че са се опитали да завъртят вектора и да изтрият всеки обект в деструктора на A, но са получили segfault.... но те няма да предоставят код. Така че използвах моите ESP правомощия, за да открия проблема и им предоставих правилното решение. - person Edward Strange; 25.06.2010
comment
@Georg: Благодаря за пояснението, това ме побърква. Странно, не помня този цитат, може би трябва да го прочета отново... - person ; 25.06.2010

Искате да избегнете съхраняването на указатели в контейнери, когато трябва да управлявате живота им.

Ако това е единственият истински собственик и е гарантирано, че ще бъде последният за почистване, тогава бих се съгласил

std::vector<B> vect;

Ако имате различни препратки и времена на живот, които може да се припокриват, тогава shared_ptr би бил по-добър (std::tr1 или boost в зависимост от компилатора)

std::vector< boost::shared_ptr<B> > vect;
person Greg Domjan    schedule 24.06.2010

От вашето описание звучи, че вашият vect член от клас A трябва да има доживотна собственост върху данните, посочени от B*.

Бих препоръчал просто да промените тази декларация на

vector< std::tr1::shared_ptr<B> > vect;

РЕДАКТИРАНЕ: заменен auto_ptr със std::tr1::shared_ptr

person njsf    schedule 24.06.2010
comment
vector плюс auto_ptr е лоша идея: gotw.ca/publications/using_auto_ptr_effectively.htm - person Josh Kelley; 25.06.2010
comment
auto_ptr не е подходящ за стандартни контейнери, поради странното му поведение при копиране. - person Mark Ransom; 25.06.2010
comment
std::tr1::shared_ptr би било по-добре. - person ; 25.06.2010