2011-01-13 4 views
2

У меня действительно странная проблема с векторами stl, в которых неправильный деструктор вызывается для правильного объекта, когда я вызываю метод стирания, если это имеет смысл.
Мой код выглядит примерно так:
Странная проблема с векторами

for(vector<Category>::iterator iter = this->children.begin(); iter != this->children.end(); iter++) 
    { 
     if((*iter).item == item) 
     { 
      this->children.erase(iter); 
      return; 
     } 
     ------------------------- 
    } 

Это просто простая функция, которая находит элемент в вектор, который имеет некоторый элемент, который нужно найти, и удаляет указанный элемент из вектора. Моя проблема в том, что, когда вызывается функция стирания, и, таким образом, объект, на который указывает итератор, уничтожается, вызывается неправильный деструктор. Более конкретным является деструктор последнего элемента в векторе, а не фактический удаляемый объект. Таким образом, память удаляется из неправильного объекта, который по-прежнему будет элементом в векторе, а фактический объект, который удаляется из вектора, все еще сохраняет всю свою память.
costructor объекта выглядит следующим образом:

Category::Category(const Category &from) 
{ 
    this->name = from.name; 
    for(vector<Category>::const_iterator iter = from.children.begin(); iter != from.children.end(); iter++) 
     this->children.push_back((*iter)); 

    this->item = new QTreeWidgetItem; 
} 

И деструктор

Category::~Category() 
{ 
    this->children.clear(); 
    if(this->item != NULL) 
    { 
     QTreeWidgetItem* parent = this->item->parent(); 
     if(parent != NULL) parent->removeChild(this->item); 
     delete this->item; 
    } 
} 
+1

Как выглядит ваш оператор присваивания копий? –

+0

Рабочим минимальным примером было бы гораздо больше приветствоваться. – karlphillip

+0

У меня нет оператора копирования, я его не использую. –

ответ

5

Когда вы удаляете свой элемент из вектора, каждый элемент после его копирования (используя оператор присваивания) в предыдущее пятно в векторе. Как только это будет завершено, последний элемент в векторе будет разрушен. Это может быть причиной того, что вы видите, что ваш последний элемент разрушен. Правило номер один при использовании STL заключается в обеспечении правильности семантики копии для вашего объекта.

Вы должны рассмотреть писать оператор присваивания:

Category & operator =(const Category & other); 

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

+0

Да, я не знал, что так работает std :: vector. Я создал оператор присваивания, и он действительно вызван перед деструктором. Я думаю, я должен переключиться на вектор указателей, а не объектов. –

+0

Будьте осторожны, у вас есть необработанный указатель, и вам нужно понять влияние операций копирования на этот указатель, особенно учитывая ваш деструктор. Подумайте об этом, вектор копирует один в другой после удаления и уничтожает оригинал в вашем текущем коде, указатель удаляется (я предполагаю, что вы не можете создать новый виджет на копии, так как это элемент пользовательского интерфейса ?) и теперь ваш элемент в векторе держится за что-то, что было удалено ... – Nim

0

Вы, вероятно, следует использовать стандартные алгоритмы.

Основная проблема, которую я вижу, заключается в том, что ваш деструктор категории запрашивает у его родительского вектора его удаление. Это не может быть прав, деструктор происходит только тогда, когда вектор уже удаляет его.

std :: vector использует размещение-новое, поэтому вы сразу вызываете своего деструктора. Я не знаю, каким будет эффект, чтобы вернуться к этому вектору.

Удалите из деструктора линию if (parent != NULL) parent->removeChild(this->item). Это не то, что вы хотите.

+0

Мне тоже нужно было сделать двойной перевод на эту строку, но на самом деле это удаление одного виджета из другого - что вполне приемлемо ... – Nim

+0

@Nim Проблема в том, что удаление элемента из вектора автоматически вызывает его конструктор (у вас есть вектор объектов, а не указателей. Поэтому объекты разрушаются). Конечно, если вы стираете «посередине», на самом деле он загружает копии, но последствия будут одинаковыми, объект с вашим элементом будет удален. И затем в деструкторе вы вернетесь к вектору и попросите его удалить элемент. – CashCow

+0

'removeChild()' является частью интерфейса * Qt *, я не считаю, что это метод, который запускает стирание из вектора. – Nim

0

Ожидаемое поведение. В моей реализации (и я догадываюсь, на вашем) при стирании элемента из вектора в него назначаются элементы из n + 1 в конец и уничтожается самый последний элемент.

Используйте std::list, если вы этого не хотите.

Демо:

#include <iostream> 
#include <vector> 
struct Category 
{ 
     int item; 
     Category(int n=0) : item(n) {} 
     ~Category() { std::cout << "Category " << item << " destroyed\n"; } 
}; 
int main() 
{ 
     std::vector<Category> children(3); 
     children[0] = Category(0); 
     children[1] = Category(1); 
     children[2] = Category(2); 

     int item = 0; 
     std::cout << " beginning the loop \n"; 
     for(std::vector<Category>::iterator iter = children.begin(); 
             iter != children.end(); ++iter) 
     { 
      if(iter->item == item) 
      { 
        children.erase(iter); // prints "Category 2 destroyed"! 
        break; 
      } 
     } 
     std::cout << " loop done \n"; 
} // this will print "Category 1 destroyed" and "Category 2 destroyed" 

И да, явно erase/remove_if является более удобным для чтения, чем петли.

Смежные вопросы