[완료] std::list 에서의 소멸자 호출??
글쓴이: bearchit / 작성시간: 금, 2006/12/22 - 6:11오후
일단 소스부터 보시죠...
#include <iostream> #include <list> using namespace std; // std::list에 삽입하기 위한 클래스 class Test { public: // 생성자의 가장 중요한 임무는 int 타입의 동적메모리를 생성하는 것입니다. Test() { cid = id; cout << "[" << cid << "] Constructor called" << endl; id++; pa = new int; } // 소멸자는 생성자에서 생성된 int 타입의 메모리를 해제합니다. ~Test() { cout << "[" << cid << "] Destructor called" << endl; delete pa; } protected: int *pa; // int 타입의 동적메모리를 참조할 포인터 static int id; // 객체에 id를 할당하기 위한 카운터 int cid; // 생성된 객체의 id public: int get_cid() const { return cid; } // 객체 id 반환 }; int Test::id = 0; typedef list<Test> TestList; int main() { TestList list; Test a, b, c; // std::list에 생성한 3개의 객체를 삽입합니다 list.insert(list.end(), a); list.insert(list.end(), b); list.insert(list.end(), c); // std::list에 담긴 모든 객체의 id를 출력합니다 TestList::iterator it; for(it=list.begin(); it!=list.end(); it++) cout << (*it).get_cid() << endl; // a, b, c 해제 및 list 해제 return 0; }
위의 소스에서처럼 내부에서 동적메모리 할당을 사용하는 클래스 Test를 만들었습니다.
Test의 객체들을 std::list에 삽입하려고 하는데, std::insert() 호출시,
위치를 리스트의 마지막(std::list.end())으로 지정하고 삽입하면,
list 해제시 오류가 발생합니다.
실행결과
[0] Constructor called [1] Constructor called [2] Constructor called 0 1 2 [2] Destructor called [1] Destructor called [0] Destructor called [0] Destructor called *** glibc detected *** ./dest: double free or corruption (fasttop): 0x0804b008 ***
실행결과를 보시면 아시겠지만, 프로그램이 종료될 때 list가 해제되면서 가장 처음에 삽입한 0번째 Test 객체가 두번 해제됩니다. 동적으로 할당한 메모리를 두 번 해제하면서 런타임 오류가 발생하는 듯한데, 혹시 std::list 클래스에서 담겨있는 객체의 소멸자를 호출하면서 발생하는 일인가요?
제가 알기로는 std::list 클래스에서 직접 소멸자를 호출하지 않는것으로 알고 있는데...
std::list에 객체 삽입시 위치를 std::list.begin()으로 하면 문제는 해결됩니다만, 리스트의 순서가 거꾸로 됩니다.
얼마전에 프로젝트를 수행하다가 이 문제를 만났다가 결국 해결하지 못하고 우회하는 방향으로 프로그램을 작성했는데, 알고 넘어가야겠다 싶어서요.
Forums:
list.begin() 을 쓰면
list.begin() 을 쓰면 문제가 해결된다고 하셨는데, 사실은 해결된 것이 전혀 아닙니다. 동적 할당된 메모리는 여전히 두 번씩 해제되고 있으나 라이브러리가 잡아내지 못할 뿐입니다. (해제되는 메모리의 주소를 직접 찍어보세요.) 질문하신 내용은 흔한 문제라서 C++ FAQ에 들어가 있습니다.
정확하게 뭐가 문제인지는 할당 연산자와 복사 생성자를 추가하다 보면 자연히 아시게 될 겁니다. :)
흔히 일으키는 오류
흔히 new로 할당하면 모조건 delete 시켜야 하는것으로 생각합니다.
그런데 그건 컴파일러에 따라 다른거죠. 그리고 new로 할당하는것이 클래스인지 멤버변수인지에 따라서도 다르게 생각해야하구요.
윗분께서 링크해주신 faq에 destructor쪽 보시면 이해되실꺼에요.
----------------------------------------------------------------------
인생 뭐있어? 백수로 사는거야~ 가는거야~
----------------------------------------------------------------------
인생 뭐있어? 백수로 사는거야~ 가는거야~
위에 답변에 덧붙여,
위에 답변에 덧붙여, stl 컨테이너는 값 복사를 기준으로 합니다.
메인 함수를
같이 해보시면 금방 아실 겁니다. push_front, push_back 이 기본으로 제공되니 간편하게 사용하세요:)
deep copy 와 shallow copy
deep copy 와 shallow copy 의 문제인데요.
list에 있는 test class 의 객체 들은 정상적으로 한번씩 소멸자가 호출되고 끝난겁니다.
마지막 소멸자 호출은 list 에 있는 객체가 아니고 main 함수는 있는 스택에 생선된 a, b, c 의 소멸자 호출인데요.
a, b, c를 list 에 insert 할 때 shallow copy 에 의해서 a, b, c 에 있는 맵버변수 pa 가 복사가 되는데요.
이때 스택의 a->pa 와 list에 복사된 a->pa 는 동일한 heap 을 가르키게 됩니다.
이런 상태에서 main 함수가 종료되면서 list에서 a가 삭제되면의 list에 있는 a->pa 가 delete 되고
나중에 main 의 스택에 있는 a가 삭제되면서 이미 list 의 a->pa 를 다시 스택의 a->pa 를 삭제하려는 시도가
발생하게 되서 double free 가 되는겁니다.
해결법은 test class 의 복사 생성자와 대입 연산자를 deep copy가 되도록 오버라이딩 하면됩니다.
네 분 모두
네 분 모두 감사합니다.
덕분에 많이 배워갑니다~ ^^
자료구조 시간에 교수님께서 마르고 닳도록 말씀하셨던
shallow/deep copy 문제였군요 ^^;;
Copy constructor를 추가하고 operator = 를 오버라이딩 했더니 말끔해졌습니다.
나중을 위해 소멸자도 가상소멸자로 만들었구요.
도움 주셔서 정말로 감사드립니다 ^^
실행결과
댓글 달기