2011-01-13 3 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; 
     } 
     ------------------------- 
    } 

그것은 검색 할 몇 가지 항목을 가지고있는 벡터의 요소를 발견하고, 벡터의 요소 말했다 제거합니다 단순한 기능입니다 :
내 코드는 다음과 같이 보입니다. 내 문제는 erase 함수가 호출되어서 iterator가 가리키고있는 객체가 파괴되고, 잘못된 소멸자가 호출되는 것보다 낫다. 더 구체적으로 벡터의 마지막 요소의 소멸자가 호출되고 실제 객체가 제거되지 않습니다. 따라서 메모리는 여전히 벡터의 요소가 될 잘못된 개체에서 제거되고 벡터에서 제거 된 실제 개체는 여전히 모든 메모리가 그대로 유지됩니다.
개체의 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을 사용할 때 규칙 1은 개체의 복사 의미가 올바른지 확인하는 것입니다.

당신은 할당 연산자를 작성 고려해야한다 :이 비록

Category & operator =(const Category & other); 

을 간단하지 않을 수는 소리, 개체를 복사 벡터에 여러 번 파괴 될 것입니다 고려.

+0

예, 실제로 이것이 std :: vector가 작동하는 방식을 전혀 모릅니다. 나는 할당 연산자를 만들었고 실제로 소멸자보다 먼저 호출되었습니다. 객체가 아닌 포인터 벡터로 전환해야한다고 생각합니다. –

+0

주의 할 점은 미가공 포인터가 있고 특히 소멸자를 고려할 때 그 포인터에 대한 복사 연산의 영향을 이해해야합니다. 이 점에 대해 생각해보십시오. 벡터는 지우기 후에 하나를 복사하고 원본을 파괴합니다. 현재 코드에서 포인터가 삭제됩니다 (UI 요소이므로 복사시 새 위젯을 만들 수 없다고 생각합니다). ?) 이제 벡터의 요소가 삭제 된 항목을 유지합니다. – Nim

0

소멸자.

내가보기에 가장 중요한 문제는 카테고리의 소멸자가 부모 벡터에게이를 제거하도록 요청한다는 것입니다. 그건 옳지 않습니다. 소멸자는 벡터가 이미 그것을 제거하고있을 때에 만 발생합니다.

std :: vector는 placement-new를 사용하므로 소멸자를 직접 호출합니다. 이 시점에서 어떤 효과가 벡터로 돌아갈 지 알 수 없습니다.

소멸자에서 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 루프보다 더 읽을 수 있습니다.