Я определил класс C ++ со следующим заголовочным файлом:
class EarleyParser
{
public:
EarleyParser();
virtual ~EarleyParser();
void initialize( string filePath, bool probabilityParse );
private:
bool probabilityParser;
typedef unordered_map< string, list<Production>* > productionHashTable;
productionHashTable earlyHashTable;
};
Как вы можете видеть, элемент-член класса является unordered_map
ключевой элемент которого является строкой, а элемент содержимого является указателем на список объектов другого класса с именем Production
(не против, это может быть что угодно).
Мой вопрос заключается в том, должен ли я оставить деструктору по умолчанию для освобождения выделенной памяти, или мне следует вручную проверить хеш-таблицу и удалить все ее элементы.
Во втором случае, что будет процедура? Вызов этого для каждого элемента будет в порядке?
EarleyParser::productionHashTable::const_iterator got = this->earlyHashTable.find( "key" );
delete[] got->second;
Вам необходимо уточнить, кому принадлежит list<Production>
объекты, принадлежащие EarlyParser
, Если EarlyParser
владеет ими, то вам нужно освободить ресурсы. Вы можете сделать это, перебирая список и вызывая delete
на каждый разыменованный итератор (не delete[]
). Или вы можете хранить unique_ptr<list<Production>>
вместо. С другой стороны, самое простое решение — хранить list<Production>
если у вас действительно нет очень веских причин для хранения указателей.
Если вы собираетесь хранить указатели на что-либо на своей карте, вам придется вручную просмотреть карту и удалить каждый из них. Обычно в классе вы придерживаетесь RAII (Resource Acquisition is Initialization) и конструируете вещи в конструктор и уничтожить в деструктор
for (;;)
delete map->second; //it's not an array of lists
Однако указатель на контейнер НЕ является хорошей идеей. Зачем вам нужен указатель? Какую проблему вы пытаетесь решить, используя указатель на list
?
Использование умного указателя, такого как std::unique_ptr
это гораздо лучшая идея, чем сырые указатели. Необработанные указатели должны быть последним средством, а не первым, что вы берете, когда не можете придумать ничего лучшего.
Использовать:
typedef unordered_map< string, std::unique_ptr<list<Production> > > productionHashTable;
вместо. Тогда вам не нужно беспокоиться об управлении памятью.
Деструктор, синтезированный компилятором, не будет удалять динамически размещенные списки, которые вы поместили на карту, поэтому вы должны сделать это самостоятельно. В этом случае вы можете просто перебрать свою карту и удалить second
член каждого элемента:
EarleyParser::~EarleyParser() {
for ( productionHashTable::iterator i = earlyHashTable.begin(); i != earlyHashTable.end(); ++i )
delete i->second;
}
Лучшим подходом было бы размещать списки на карте, а не указатели на списки. В этом случае компилятор автоматически позаботится об уничтожении, как в:
typedef unordered_map< string, list<Production> > productionHashTable;
Деструктор Unordered_map на самом деле вызывает деструкторы объекта, который он имеет, что означает, что деструктор list
будет называться
Деструктор std :: list имеет это примечание:
Обратите внимание, что если элементы являются указателями, объекты, на которые указывает указатель, не уничтожаются.
Таким образом, это означает, что вам придется очистить эту память самостоятельно. Да, пройти через контейнер и удалить элементы 1by1 можно. Как отмечали другие респонденты, держать указатели в таком виде не очень хорошая идея.
Поскольку вы используете необработанный указатель на std::list
вам придется удалить его самостоятельно во время жизненного цикла карты или при очистке EarleyParser
объект в его деструкторе.
Вы можете использовать что-то вроде этого в вашем деструкторе:
for ( auto it = productionHashTable.begin();
it != productionHashTable.end(); ++it )
{
delete it->second;
}
productionHashTable.clear()
Обратите внимание, что последняя строка не является строго необходимой, так как она будет очищена в любом случае, так как EarleyParser
объект уничтожен, но очевидно, что вы не должны использовать значения на карте после того, как удалили их!