2009-12-04 4 views
4

내 프로그램을 실행할 때 모든 것이 잘됩니다. 내가 체크 아웃C++ 소멸자 엉망, 디버그 불가능

*** glibc detected *** ./streamShare: double free or corruption (fasttop): 0x08292130 *** 
======= Backtrace: ========= 
/lib/tls/i686/cmov/libc.so.6[0xcc2ff1] 
/lib/tls/i686/cmov/libc.so.6[0xcc46f2] 
/lib/tls/i686/cmov/libc.so.6(cfree+0x6d)[0xcc779d] 
/usr/lib/libstdc++.so.6(_ZdlPv+0x21)[0x1c86f1] 
./streamShare[0x804be7f] 
./streamShare[0x804be3e] 
./streamShare[0x804abc0] 
./streamShare[0x804a5f2] 
./streamShare[0x804a1c4] 
./streamShare[0x804a1d7] 
./streamShare[0x804a46a] 
./streamShare[0x804ba45] 
./streamShare[0x804b49c] 
./streamShare[0x804ac68] 
./streamShare[0x804ac48] 
./streamShare[0x804a676] 
./streamShare[0x804a237] 
./streamShare[0x8049a3f] 
./streamShare[0x804d2e5] 
./streamShare[0x804d34d] 
/lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe6)[0xc6eb56] 
./streamShare[0x8049361] 

함수 반환, 프로그램의 모든 개체가 자동으로 instatied 곳 때, 그런 일이 : 마지막에이 출력합니다. 어쨌든이 객체에 대한 소멸자를 정의하지 않았고 STL 컨테이너와 TR1 shared_ptr을 사용하려고했습니다. 모든 것이 기본 소멸자에서 발생합니다. 어디에서 헤어 지는지 알 수있는 방법이 있습니까? 내 말은, 어떤 물체가 혼란을 일으키는 지 알고 싶습니다. 다음 컨테이너와 공유 포인터를 사용하고 있습니다.

typedef std::tr1::shared_ptr<messageListener> mlsptr; 

typedef std::map<const char*, mlsptr, ltstr> CONSTCHP2MSLST; 

messageListener에는 디스트 리뷰터가 없습니다. 그리고이 벡터의 두 :

std::vector<MSG> queueto1; 

MSG 소멸자입니다 : 전에 문제를 준 적이

MSG::~MSG() { 
    destroy(); 
} 

void MSG::destroy() { 
    if (payload != NULL) 
     delete[] payload; 
    payload = NULL; 
    payloadLen = 0; 
} 

나는이 그렇지 이제까지 ...

모든의 recomendations을 어떻게 추적 할 수 있어야 이 문제?

valgrind ./streamShare -v 
==25795== Memcheck, a memory error detector 
==25795== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al. 
==25795== Using Valgrind-3.5.0-Debian and LibVEX; rerun with -h for copyright info 
==25795== Command: ./streamShare -v 
==25795== 
==25795== Invalid free()/delete/delete[] 
==25795== at 0x402454D: operator delete(void*) (vg_replace_malloc.c:346) 
==25795== by 0x804BCC0: std::tr1::_Sp_deleter<streamShare::messageListener>::operator()(streamShare::messageListener*) const (shared_ptr.h:97) 
==25795== by 0x804BC7F: std::tr1::_Sp_counted_base_impl<streamShare::messageListener*, std::tr1::_Sp_deleter<streamShare::messageListener>, (__gnu_cxx::_Lock_policy)2>::_M_dispose() (shared_ptr.h:75) 
==25795== by 0x804AAF7: std::tr1::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() (boost_sp_counted_base.h:140) 
==25795== by 0x804A58D: std::tr1::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() (shared_ptr.h:153) 
==25795== by 0x804A173: std::tr1::__shared_ptr<streamShare::messageListener, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() (shared_ptr.h:358) 
==25795== by 0x804A186: std::tr1::shared_ptr<streamShare::messageListener>::~shared_ptr() (shared_ptr.h:834) 
==25795== by 0x804A405: std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> >::~pair() (stl_pair.h:68) 
==25795== by 0x804D3D0: __gnu_cxx::new_allocator<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >::destroy(std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> >*) (new_allocator.h:115) 
==25795== by 0x804D337: std::_Rb_tree<char const*, std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> >, std::_Select1st<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >, streamShare::ltstr, std::allocator<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > > >::_M_destroy_node(std::_Rb_tree_node<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >*) (stl_tree.h:383) 
==25795== by 0x804D29B: std::_Rb_tree<char const*, std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> >, std::_Select1st<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >, streamShare::ltstr, std::allocator<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > > >::_M_erase(std::_Rb_tree_node<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >*) (stl_tree.h:972) 
==25795== by 0x804D27B: std::_Rb_tree<char const*, std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> >, std::_Select1st<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >, streamShare::ltstr, std::allocator<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > > >::_M_erase(std::_Rb_tree_node<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >*) (stl_tree.h:970) 
==25795== Address 0x42c3358 is 0 bytes inside a block of size 8 free'd 
==25795== at 0x402454D: operator delete(void*) (vg_replace_malloc.c:346) 
==25795== by 0x804BCC0: std::tr1::_Sp_deleter<streamShare::messageListener>::operator()(streamShare::messageListener*) const (shared_ptr.h:97) 
==25795== by 0x804BC7F: std::tr1::_Sp_counted_base_impl<streamShare::messageListener*, std::tr1::_Sp_deleter<streamShare::messageListener>, (__gnu_cxx::_Lock_policy)2>::_M_dispose() (shared_ptr.h:75) 
==25795== by 0x804AAF7: std::tr1::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() (boost_sp_counted_base.h:140) 
==25795== by 0x804A58D: std::tr1::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() (shared_ptr.h:153) 
==25795== by 0x804A173: std::tr1::__shared_ptr<streamShare::messageListener, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() (shared_ptr.h:358) 
==25795== by 0x804A186: std::tr1::shared_ptr<streamShare::messageListener>::~shared_ptr() (shared_ptr.h:834) 
==25795== by 0x804A405: std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> >::~pair() (stl_pair.h:68) 
==25795== by 0x804D3D0: __gnu_cxx::new_allocator<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >::destroy(std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> >*) (new_allocator.h:115) 
==25795== by 0x804D337: std::_Rb_tree<char const*, std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> >, std::_Select1st<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >, streamShare::ltstr, std::allocator<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > > >::_M_destroy_node(std::_Rb_tree_node<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >*) (stl_tree.h:383) 
==25795== by 0x804D29B: std::_Rb_tree<char const*, std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> >, std::_Select1st<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >, streamShare::ltstr, std::allocator<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > > >::_M_erase(std::_Rb_tree_node<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >*) (stl_tree.h:972) 
==25795== by 0x804D27B: std::_Rb_tree<char const*, std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> >, std::_Select1st<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >, streamShare::ltstr, std::allocator<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > > >::_M_erase(std::_Rb_tree_node<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >*) (stl_tree.h:970) 
==25795== 
==25795== 
==25795== HEAP SUMMARY: 
==25795==  in use at exit: 0 bytes in 0 blocks 
==25795== total heap usage: 22 allocs, 30 frees, 496 bytes allocated 
==25795== 
==25795== All heap blocks were freed -- no leaks are possible 
==25795== 
==25795== For counts of detected and suppressed errors, rerun with: -v 
==25795== ERROR SUMMARY: 8 errors from 1 contexts (suppressed: 19 from 8) 
+3

http://valgrind.org/ 설치 - Valgrind에서 실행중인 프로그램으로 출력하십시오. – ephemient

+1

1) 샘플 코드에서'payload'는 무엇입니까? 2) 맵에 넣은'shared_ptr'을 초기화하는 방법을 보여주십시오. –

+2

MSG의 복사 생성자와 할당 연산자를 게시하십시오. MSG에 그러한 것이없는 경우 문제가 있습니다. –

답변

27

Valgrind 출력으로 판단 할 때 shared_ptr이 가리키는 객체가 두 번 삭제되는 것이 문제입니다. 같은 원시 포인터 두 shared_ptr을 초기화 할 경우 그지고의 하나의 가능성은 예 :

int* p = new int(123); 
shared_ptr<int> sp1(p); 
shared_ptr<int> sp2(p); 

shared_ptr 마법하지 않고, 객체가 이미 일부 소유하고 돌봐를 요구하는 경우는 알 수 없다 다른 관련이없는 shared_ptr, 당신이주는 모든 것은 원시 포인터입니다. 위의 예제에서 각 shared_ptr은 1로 초기화 된 자체 참조 카운터를 생성합니다. 그들이 죽을 때마다, 각각은 자신의 카운터를 감소시키고, 그것이 0임을 확인하고, 객체를 삭제하여 이중 삭제를 생성합니다. 나는 네 사건이 유사하다고 생각해. 벡터에 추가 된 shared_ptr 개체를 초기화하는 데 사용 된 코드를 표시하면이를 확인할 수 있습니다.

이 이후

[편집] 는 이제 사고의 원인이 될 날이 제대로 shared_ptr를 사용하는 방법에 대한 비트 정교한 수 있도록 검증한다.

우선, 문제의 본질. shared_ptr은 모든 C++ 유형에서 작동하며 참조 카운팅 의미를 제공합니다. 명백한 문제는 대부분의 유형이 참조 카운터를 저장할 공간을 제공하지 않는다는 것입니다 (예 : shared_ptr<int> - 내부에 "int"이라는 추가 공간 없음). 이 문제를 해결하기 위해 모든 공유 개체에 대해 참조 카운터가 포함 된 별도의 메모리 블록이 할당됩니다. 이것은 원시 포인터에서 shared_ptr을 만들 때마다 수행됩니다. 그런 다음 shared_ptr 개체 자체는 원래 원시 포인터와 참조 카운터에 대한 포인터를 저장합니다 (원시 포인터보다 더 "뚱뚱한"이유는 간단하게 sizeof으로 확인할 수 있음). 복사본 생성자 또는 할당 연산자를 사용하여 shared_ptr을 다른 것으로 생성하면 참조 카운터에 포인터를 복사하므로 서로 생성 된 shared_ptr 인스턴스는 모두 단일 카운터를 유지하고 올바른 삭제를 보장합니다. 그러나 동일한 객체 (같은 원시 포인터에서 두 개 이상의 포인터가 만들어진 곳)에 두 개의 서로 관련이없는 "계열"인 객체가있는 경우 해당 "패밀리"는 서로에 대해 알지 않으며 별도로 다시 계산할 것이고 각 객체는

실제로이 의미는 shared_ptr을 사용할 때 특정 규칙을 준수해야한다는 것입니다. 그것들은 당신이 사용하는 구현에 의존합니다. std::tr1::shared_ptr, 또는 그 이상 부스트 버전으로

은, 객체 할당에 대한 유일한 완전히 안전 패턴은 이것이다 : 즉

shared_ptr<T> x(new T(...)); 

, new의 결과는 즉시shared_ptr에 투입해야한다 - 당신은 할 수 있습니다 후자를 원하는만큼 복사하십시오.

합리적으로 안전 패턴이 있습니다 :

auto_ptr<T> x(new T); 
... 
shared_ptr<T> y(x); 

shared_ptrauto_ptr에서 초기화 보통의 소유권 이전-하고 (그래서 그들이 제대로 따라하고 같은) 후자의 의미를 구현 보장 객체에 대한 하나의 auto_ptr 만 존재해야합니다. 따라서, 그것으로부터 shared_ptr을 만드는 것이 안전합니다.

간혹 포인터 소유권 이전을 나타 내기 위해 auto_ptr을 사용하지 않는 C++ 라이브러리를 처리해야하지만 특정 기능에 대한 의도를 문서화하기 만하면됩니다. 이러한 경우에 그것은 또한 shared_ptr를 사용하는 것이 안전해야하지만, 물론 당신이

C++ 0X std::shared_ptr에서

boost::shared_ptr의 최신 버전에 ... 올바르게 문서를 이해했는지 확인해야한다, 거기 helper는 공유 객체의 올바른 인스턴스을 보장하기 위해 제공 :

shared_ptr<int> p = make_shared<int>(123); 

make_shared<T>()의 반환 형식은 이미 shared_ptr<T>입니다 어떤 시점에서 당신이 뭔가 잘못을 얻을 수있는 기회를 줄이고, 코드에서 원시 포인터를 상대하고 그래서.

+0

S ** T, 당신은 문제 남자를 때렸다 ... 나는 shared_ptr을 처음 사용했다. 미안해. : DI는 많은 shared_ptr에 동일한 원시 포인터를 전달하고있었습니다 ... 공유 포인터를 일반 포인터로 대체했기 때문에 이것이였습니다. 몇 가지를 변경하는 것을 잊었습니다 !!!! 감사! – gotch4

2

첫 번째 단계는 당신이 더 많은 정보를 디버깅을 얻을 수 있도록 -g 3 스위치를 사용하여 프로그램을 컴파일하는 것입니다 : 여기

Valgrind의 출력입니다 : 나는

편집

은 ... 우둔입니다.

계속 진행할 필요는 없지만 벡터 재배치가 재 할당 될 때 ~MSG()이 호출 될 가능성이 있습니다.

#include <vector> 

struct MSG { 
    MSG(int count); 
    ~MSG(); 
    void destroy(); 
    char* payload; 
    int payloadLen; 
}; 

MSG::MSG(int count): payload(new char[count]), payloadLen(count) {} 

MSG::~MSG() { 
    destroy(); 
} 

void MSG::destroy() { 
    if (payload != NULL) 
     delete[] payload; 
    payload = NULL; 
    payloadLen = 0; 
} 

std::vector<MSG> queueto1; 

int main() { 
    queueto1.push_back(MSG(10)); 
    return 0; 
} 

And G++ says :

여기에 코드의 가장 간단한 버전의

블록 두 번

을 해제 종료 함 : 이제 ExitFailure 127

push_back(MSG(10)) 인 경우 불리고있다. 임시 MSG 인스턴스를 만든 다음 벡터에있는 항목을 설정하려면 기본 복사 생성자를 사용하여 임시에 ~MSG() (벡터의 항목이 가리키는 페이로드를 삭제)을 호출하기 전에 항목을 벡터에 설정합니다.

복사 생성자 MSG(const MSG& copy) ~ 전송 소유권을 구현할 수 있지만 사본에는 const_cast가 필요하며 매우 지저분합니다.MSG::payload 공유 포인터, 또는 queueto1 메모리 할당 및 손상 문제는 당신이 "페이로드"포인터의 printf valgrind 또는 Rational Purify

+0

불행히도 벡터가 파손되면 완료됩니다. – gotch4

+0

정확하게 게시 한 백 트레이스는 디버그 정보가 누락되었습니다. 또한 -Ox (-O1 -O2 또는 사용 된 모든 것) 옵션을 제거하십시오. gdb를 사용 했지, 그렇지? – Frunsi

+0

최적화 및 디버깅 수준이 최대가 아닙니다 ... – gotch4

1

입니다 :

두 쉬운 옵션을 사용할 수 있습니다 삭제와 같은 카운터가 발생합니다. 다음 두 번 무료로 일어나는 일을 볼 수 있으며 그것을 삭제합니다 ...

+0

나는 valgrind를 시도했다 ... STL 템플릿에 관한 많은 것들을 말하며 문제를 지적하지 않는다 ... – gotch4

0

을 사용할 수 있습니다 추적하는 std::vector<MSG*>

0

Valgrind에 따르면 귀하의 CONSTCHP2MSLST 인스턴스가 파괴되면서 충돌이 발생합니다. 문제는 messageListener 구현에 있습니다. 해당 컨스트럭터를 게시하고 해당 클래스의 생성자를 복사 할 수 있습니까?

0

측면 문제 : 소멸자를 정의하지 않고 STL 컨테이너와 공유 포인터를 사용했다고 말했습니까? 컨테이너와 공유 포인터가하는 일은 소멸자에게 전화하는 것입니다. 그들은 소멸자를 대체하지 않습니다. 그들은 소멸자를 언제 사용해야하는지 알아낼 필요성을 대체합니다.

이것은 클래스에 기본 소멸자가 있다는 것을 의미합니다. 기본 소멸자는 기본 클래스와 멤버에있는 소멸자를 호출하는 것으로 구성됩니다. 클래스가 실제로 스마트 포인터 등을 통한 리소스를 소유하지 않고 다형성을 사용하지 않는 경우 작동 할 수 있습니다. "STL 구조체와 스마트 포인터를 사용하므로 소멸자가 필요하지 않습니다."보다 더 많은주의가 필요합니다.

class Aclass B: public A을 가지고 당신이 이제까지 B 가리키는 A에 대한 포인터가있는 경우 (원시 포인터 스마트 포인터를, 그것은 문제가되지 않습니다), 당신은 가상 소멸자가 필요합니다. 그렇지 않으면 A에 대한 포인터가 삭제되면 A의 모든 하위 클래스와 멤버가 삭제되고 B에 추가 된 모든 항목은 무시됩니다. 따라서 이런 방식으로 사용될 기본 클래스에 가상 소멸자가 있어야하며 안전을 위해 가상 함수가있는 기본 클래스에 가상 소멸자가 있어야합니다. 소멸자에서 제거 할 것이 없더라도 A의 정의에 virtual A::~A() {}이 있어야합니다.

0

위의 소멸자에 대한 요점은 정확합니다. 단, 소멸자를 정의하지 않으면 항상 클래스에 비어 있지 않은 비 소멸자가 있습니다. 소멸자가 가상 ​​일 필요가있는 경우, 적어도 첫 번째 기본 클래스 소멸자는 가상이어야하며 물론 모든 파생 클래스는 자동으로 가상이지만 가상 키워드는 가독성을 위해 사용해야합니다.

스마트 포인터를 사용하는 이중 자유형 사례에서 자주 볼 수있는 오류 중 하나는 공유 포인터 인스턴스가 참조로 전달되지 않도록하는 것입니다. 또는 단일 스레드 구현에서 참조로 전달되는 경우 인스턴스는 다음과 같습니다. 값에 의해 즉시 복사 됨 - 일부 STL 컨테이너에서 공유 포인터가 발생 함.

참조로 전달되거나 원시 포인터가 추출되어 해제되면 이중 해제가 가능합니다.

하나의 마지막 문제는 구현이 다중 스레드 인 경우 공유 포인터 구현이 참조 카운트에 대해 threadsafe 구현을 가져야한다는 것입니다. 그렇지 않으면 double free 또는 누수가 발생할 수 있습니다.