0

STL 컨테이너 객체를 참조 대 값으로 이동하는 방법에 대한 오해가 있다고 생각합니다. 합니다 (Ts 벡터에 값을 인쇄하려고) 루프에 대한 두 번째의값에 의한 STL 컨테이너 객체 대 참조로

#include <vector> 
#include <set> 
#include <cstdio> 

class Value { 
public: 
    int x, y; 
    Value(int a, int b) { x = a; y = b; } 
}; 

class Test { 
public: 
    Test(int x, int y) { values.insert(new Value(x, y)); } 
    void add(int x, int y) { values.insert(new Value(x, y)); } 
    std::set<Value *> getValues() { return values; } 
private: 
    std::set<Value *> values; 
}; 

int main() { 
    std::vector<Test> Ts; 
    for (unsigned i = 0; i < 5; i++) { 
    Test t(0, 0); 
    t.add(i, 0); 
    Ts.push_back(t); 
    } 
    for (unsigned i = 0; i < 5; i++) { 
    for (std::set<Value *>::iterator it = Ts.at(i).getValues().begin(), ite = Ts.at(i).getValues().end(); it != ite; ++it) { 
     Value *v = *it; 
     printf("(%d, %d) ", v->x, v->y); 
    } 
    printf("\n"); 
    } 
    return 0; 
} 

이 프로그램은 세그먼테이션 폴트 (segfault) : 특히, 나는 이유는 다음과 같은 프로그램이 충돌을 이해하지 않습니다. 그러나 초기 루프를 다음과 같이 변경 한 경우 :

for (unsigned i = 0; i < 5; i++) { 
    Ts.push_back(Test(0, 0)); 
} 

그런 다음 프로그램이 잘 실행됩니다. 첫 번째 프로그램 (충돌)을 타고에 인쇄 루프를 변경하는 경우 또한, :

for (unsigned i = 0; i < 5; i++) { 
    std::set<Value *> values = Ts.at(i).getValues(); 
    for (std::set<Value *>::iterator it = values.begin(), ite = values.end(); it != ite; ++it) { 
    Value *v = *it; 
    printf("(%d, %d) ", v->x, v->y); 
    } 
    printf("\n"); 
} 

그런 다음 프로그램이 충돌하지 않습니다.

이러한 충돌을 일으키는 원인을 이해하고 프로그램 간의 차이점을 알고 싶습니다.

std::set<Value *> getValues() { return values; } 

이 멤버 함수 포인터의 당신의 세트의 사본을 반환

+0

프로그램의 메모리 누수가 있습니까? 'Test' 클래스에서'new'를 사용할 이유가 절대적으로 없습니다. – pmr

+0

@pmr 예. 이것은 필자의 큰 프로그램의 테스트 케이스 버전을 줄여서 나중에 값을 적절하게 해제합니다. –

+1

메모리가 누출되는 부분 이외에도 for 루프에'values' 세트가 여러 개 생성됩니다. 왜냐하면'getValues ​​()'가 사본을 반환하기 때문입니다. 이는 반복기가 메모리의 잘못된 위치에 도달하여 세그멘테이션 오류가 발생하는 이유 일 수 있습니다. –

답변

2

여기 찾을 수가 가장 큰 문제는 당신의 두 줄의 코드로 요약된다.

for (std::set<Value *>::iterator it = Ts.at(i).getValues().begin(), ite = Ts.at(i).getValues().end(); it != ite; ++it) 

동일한 논리 행에서이 명령은 초기화 단계에서 두 세트를 만듭니다. ite은 예상 된 집합의 최종 반복자가 아니라 새로 생성 된 다른 컨테이너의 끝 포인터입니다. 결과적으로 it != ite을 수행하면 it이 예상치 못한 메모리 위치를 가리키기 전에 도달하지 못할 가능성이 높습니다.

이제 동일한 세트의 반복자를 다루기 때문에 수정이 필요합니다. 사본이 여기에서도 발생하지만이 경우 안전한 사본입니다. 또한 원시 포인터를 저장하기 때문에 모든 사본이 얕습니다.

std::set<Value *> values = Ts.at(i).getValues(); // get copy of set 
for (std::set<Value *>::iterator it = values.begin(), ite = values.end(); it != ite; ++it) { // using iterators from the same set, this is OK 
+0

감사합니다. 첫 번째 수정이 왜 효과가 있었는지 설명 할 수 있습니까? 나는. 루프 반복자 만 남겨두고 아이템이'Ts' 벡터에 추가되는 방식을 변경합니까? 운이 좋았나요? –

+0

나는 그것을 분석해 왔지만 그럴만한 이유가 없다는 것을 발견했습니다. 유일한 차이점은 2보다는 1 요소가있는 것 같습니다. 왜 모든 것이 첫 번째 "수정"에서 여전히 작동하는지가 정의되지 않은 동작의 가장 된 경우처럼 보입니다. –