Удаление пользовательских векторов — C++

У меня есть 2 класса, скажем, A и B. У класса B есть собственный деструктор. В классе A у меня есть вектор указателей на объекты класса B. Вектор выглядит следующим образом:

vector<B*> vect;

Как получить память в деструкторе класса А? Если я прокручиваю вектор, извлекаю ли я каждый объект и использую удаление для каждого извлеченного объекта? Я попробовал это в деструкторе, но это segfaults.

Любая помощь в решении этой проблемы приветствуется. Извините, но я не могу опубликовать код.


person Sriram    schedule 24.06.2010    source источник
comment
Прежде чем мы ответим на этот вопрос. Мы должны знать, как вы помещаете вещи в вектор (объекты создаются с помощью new (это открывает еще один вопрос, кому принадлежат объекты, которые вы только что создали?)). Другой вопрос, почему вы ставите указатели в векторе. Вектор предназначен для того, чтобы взять на себя ответственность за «Объекты», поэтому, если ваш 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();

edit: если ваша программа ошибается в деструкторе, значит, ваш код неверен. Может быть, вы помещаете элемент стека по адресу в вектор, но для удаления объекта он должен быть в куче.

#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 предназначен для вашего конкретного примера, но простой std::vector<std::tr1::shared_ptr<B> > также будет работать (хотя и с большими накладными расходами и более неудобным синтаксисом).

person Josh Kelley    schedule 24.06.2010

Да, если элементы типа B* указывают на объекты, размещенные в куче, то для каждого элемента вы должны вызывать для него удаление.

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
ОП говорит, что они пытались зациклить вектор и удалить каждый объект в деструкторе 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

Судя по вашему описанию, ваш векторный член класса 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_efficiently.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