23

즉, 구현에서 개수를 어떻게 추적합니까?참조 계산 스마트 포인터의 참조 계산은 어떻게 작동합니까?

포인터 모양의 주소이고 값은 참조 번호 인 모든 shared_ptr 인스턴스가 액세스 할 수있는지도와 유사한 객체가 유지됩니까? shared_ptr을 구현해야하는 경우 이것이 내 마음에 떠오르는 첫 번째 아이디어입니다.

이러한 참조 카운팅 스마트 포인터의 경우 메모리 누수가 발생할 수 있습니까? 그렇다면 어떻게 피할 수 있습니까?

답변

58

I이 두 개의 상이한 비 침투 방법 보았다 :

  1. 스마트 포인터 참조 카운터를 포함하는 메모리의 작은 블록을 할당 . 스마트 포인터의 각 복사본은 실제 개체에 대한 포인터와 참조 카운트에 대한 포인터를받습니다. 오브젝트 포인터 외에도
  2. 는 각 스마트 포인터 특정 객체 스마트 포인터의 이중 연결리스트 형성 이전 및 다음 포인터를 포함하여 . 참조 카운트는 목록에 암시 적으로 입니다. 스마트 포인터가 복사되면 자체 목록에 을 추가합니다. 파기시, 각 스마트 포인터는 자신을 목록에서 제거합니다. 마지막으로 의 목록에 있으면 참조 된 개체도 해제됩니다.

here으로 이동하여 아래로 스크롤하면 이러한 방법을 훨씬 더 명확하게 설명하는 훌륭한 다이어그램이 있습니다.

+0

이것은 가장 정확한 답입니다. –

+2

linked-list 방식은 추가 할당을 피하지만 전역 뮤텍스가 없으면 "스레드 안전"을 만들기가 매우 어렵습니다. ("thread-safe"는 "원시 포인터처럼 쓰레드 안전하다") – curiousguy

+2

또한'make_shared'를 사용하면 할당 된 객체와 인스턴스 카운터를 하나의 메모리 블럭에 두어 추가 할당을 피할 수있다. – Ferruccio

2

아니요 shared_ptr 참조 계산을 위해 포인터를 하나 더 유지하십시오.

shared_ptr 개체의 복사본을 만들 때 참조 횟수가 포함 된 포인터를 복사하고 늘리면 포함 된 개체에 포인터를 복사합니다.

2

참조 카운팅 스마트 포인터로 메모리 누출을 만드는 것은 매우 쉽습니다. 그래프에주기가있는 오브젝트의 그래프와 유사한 구조를 생성하면됩니다. 싸이클의 오브젝트는 서로가 해제되는 것을 방지합니다. 이것은 자동으로 해결 될 수 없습니다. 예를 들어, 이중 링크 목록을 만들 때 한 번에 두 개 이상의 객체를 제거하지 않아야합니다.

3

각 스마트 포인터 개체는 공유 참조 카운트 (모든 원시 포인터에 대해 하나)를 포함합니다.

this 문서를 살펴볼 수 있습니다. 이 구현은 이것을 복제 된 별도의 객체에 저장합니다. boost's documentation을 보거나 스마트 포인터에서 Wikipedia article을 살펴볼 수도 있습니다.

+0

-1. 동일한 객체를 참조하는 모든 스마트 포인터는 * 단일 참조 카운트를 공유해야합니다 *. 참조 카운트를 보유한 객체는 첫 번째 스마트 포인터 객체에 의해 할당되고 스마트 포인터가 복사 될 때 * 포인터 (객체 자체는 아님)가 복사됩니다. –

+0

j_random_hacker에서 동의하십시오. 이 카운트는 각 미가공 포인터에 대해 고유하며 동일한 미가공 포인터를 참조하는 모든 shared_ptr에 의해 공유됩니다. 보통 smart_ptr은 두 개의 내부 ptrs를 가지고 있는데 하나는 참조 카운트를위한 것이고 다른 하나는 포인터 자체를위한 것이다. –

+0

-1 정적 변수입니다. 싱글 톤 객체에 대한 참조 카운트 된 스마트 포인터를 구현하지 않는 한 정적을 사용하여 참조 카운팅을 구현할 수 없습니다. –

2

지금까지 내가 기억하는 한, 효과적인 C++ 장에서 다루는 참조 카운팅 포인터 문제가있었습니다.

원칙적으로 참조를 증가/감소시키고 포인터 객체를 파괴하는 참조를 보유하는 클래스에 대한 포인터를 포함하는 "light"포인터 클래스가 있습니다. 참조 계산 클래스는 참조 할 객체를 가리 킵니다.

2

많은 답변은 참조 횟수가 저장되는 방식을 설명합니다 (동일한 네이티브 포인터를 보유하는 모든 shared_ptr에 대해 공유 메모리에 저장 됨). 그러나 대부분 누설 문제를 피합니다.

참조 카운트 된 포인터로 메모리를 누출하는 가장 쉬운 방법은주기를 만드는 것입니다. 예를 들어, 모든 포인터가 shared_ptr이고 두 개 이상의 요소가있는 이중 링크 된 목록은 삭제되지 않도록 보장됩니다. 외부 포인터가 해제 된 경우에도 내부 포인터는 계속 카운트되며 참조 카운트는 0에 도달하지 않습니다. 즉, 적어도 가장 순진한 구현입니다.

주기 문제에 대한 가장 쉬운 해결책은 shared_ptr (참조 카운트 된 포인터)을 개체의 소유권을 공유하지 않는 약한 포인터와 혼합하는 것입니다.

공유 포인터는 리소스 (포인터)와 추가 reference_count 정보를 공유합니다. 약한 포인터를 사용하면 참조 횟수가 두 배가됩니다. 공유 포인터 참조 횟수와 약한 포인터 참조 횟수가 있습니다. 자원은 공유 포인터 계수가 0이 될 때마다 해제되지만 마지막 weak 포인터가 해제 될 때까지 reference_count 정보는 활성 상태로 남습니다.

이중 연결 목록에서 외부 참조는 shared_ptr에 보관되지만 내부 링크는 weak_ptr입니다. 외부 참조 (shared_ptr)가 없을 때마다 목록의 요소가 해제되어 약한 참조가 삭제됩니다. 결국 모든 약한 참조가 삭제되고 각 리소스에 대한 마지막 약한 포인터가 reference_count 정보를 해제합니다.

위의 텍스트보다 혼동이 적습니다 ... 나중에 다시 시도하겠습니다.

+2

나중에 다시 시도하지 않았습니다. – xaxxon

관련 문제