스마트 포인터 (부스트) 설명


220

다음 포인터 세트의 차이점은 무엇입니까? 프로덕션 코드에서 각 포인터를 언제 사용합니까?

예를 들어 주시면 감사하겠습니다!

  1. scoped_ptr

  2. shared_ptr

  3. weak_ptr

  4. intrusive_ptr

프로덕션 코드에서 부스트를 사용합니까?

답변:


339

스마트 포인터의 기본 속성

각 스마트 포인터를 할당 할 수있는 속성이있을 때 쉽습니다. 세 가지 중요한 속성이 있습니다.

  • 전혀 소유권이 없다
  • 소유권 양도
  • 소유 지분

첫 번째는 스마트 포인터가 객체를 소유하지 않기 때문에 객체를 삭제할 수 없음을 의미합니다. 두 번째는 하나의 스마트 포인터 만 동시에 같은 객체를 가리킬 수 있음을 의미합니다. 스마트 포인터가 함수에서 반환되는 경우 소유권은 예를 들어 반환 된 스마트 포인터로 전송됩니다.

세 번째는 여러 스마트 포인터가 동시에 동일한 객체를 가리킬 수 있음을 의미합니다. 이것은 원시 포인터 에도 적용 되지만 원시 포인터에는 중요한 기능이 없습니다. 소유 여부를 정의 하지 않습니다. 모든 소유자가 객체를 포기하면 소유권 공유 스마트 포인터가 객체를 삭제합니다. 이 동작은 자주 필요하므로 공유 소유 스마트 포인터가 널리 퍼져 있습니다.

일부 소유 스마트 포인터는 두 번째 또는 세 번째를 지원하지 않습니다. 따라서 함수에서 리턴되거나 다른 곳으로 전달 될 수 없습니다. 이는 RAII스마트 포인터가 로컬에 유지되고 방금 생성 된 개체가 범위를 벗어난 후 개체를 해제하는 목적에 가장 적합합니다 .

사본 생성자를 사용하여 소유권 공유를 구현할 수 있습니다. 이것은 자연스럽게 스마트 포인터를 복사하며 사본과 원본 모두 동일한 객체를 참조합니다. 한 객체에서 다른 객체로 언어를 지원하는 다른 객체로 무언가를 전송할 수있는 방법이 없기 때문에 소유권 이전은 현재 C ++에서 실제로 구현할 수 없습니다. 함수에서 객체를 반환하려고하면 객체가 복사됩니다. 따라서 소유권 이전을 구현하는 스마트 포인터는 사본 생성자를 사용하여 소유권 이전을 구현해야합니다. 그러나 요구 사항에 이러한 스마트 포인터의 소위 "이동 생성자"동작과 호환되지 않는 컨테이너 요소의 복사 생성자의 특정 동작이 요구되므로 컨테이너에서의 사용법이 중단됩니다.

C ++ 1x는 소위 "이동 생성자"및 "이동 할당 연산자"를 도입하여 소유권 이전을 기본적으로 지원합니다. 또한 이와 같은 소유권 양도 스마트 포인터가 함께 제공됩니다 unique_ptr.

스마트 포인터 분류

scoped_ptr양도 및 공유 할 수없는 스마트 포인터입니다. 로컬로 메모리를 할당 해야하는 경우에만 사용할 수 있지만 범위를 벗어나면 다시 해제해야합니다. 그러나 원하는 경우 다른 scoped_ptr로 여전히 교체 할 수 있습니다.

shared_ptr소유권을 공유하는 스마트 포인터입니다 (위의 세 번째 종류). 참조 횟수가 계산되므로 마지막 사본이 범위를 벗어난 시점을 확인한 다음 관리 대상 개체를 해제합니다.

weak_ptr비 소유 스마트 포인터입니다. 참조 횟수를 추가하지 않고 관리 객체 (shared_ptr로 관리)를 참조하는 데 사용됩니다. 일반적으로 shared_ptr에서 원시 포인터를 가져 와서 복사해야합니다. 그러나 객체가 실제로 삭제 된 시점을 확인할 수있는 방법이 없으므로 안전하지 않습니다. 따라서 weak_ptr은 shared_ptr에서 관리하는 오브젝트를 참조하여 수단을 제공합니다. 객체에 액세스 해야하는 경우 객체 관리를 잠그고 (다른 스레드에서 객체를 사용하는 동안 shared_ptr이 객체를 해제하지 않도록) 사용할 수 있습니다. weak_ptr이 이미 삭제 된 객체를 가리키는 경우 예외를 발생시켜 알려줍니다. weak_ptr을 사용하면 주기적 참조가있을 때 가장 유용합니다. 참조 계수는 이러한 상황에 쉽게 대처할 수 없습니다.

intrusive_ptrshared_ptr과 비슷하지만 shared_ptr에 참조 카운트를 유지하지 않지만 관리되는 오브젝트에 의해 정의되어야하는 일부 헬퍼 함수에 대한 카운트를 증가 / 감소시킵니다. 이것은 이미 참조 된 객체 (외부 참조 계산 메커니즘에 의해 증가 된 참조 카운트를 가짐)가 intrusive_ptr에 채워질 수 있다는 장점이 있습니다. 참조 카운트는 더 이상 스마트 포인터 내부에 있지 않지만 스마트 포인터는 기존 포인터를 사용하기 때문입니다. 참조 카운팅 메커니즘.

unique_ptr소유권 이전 포인터입니다. 복사 할 수는 없지만 C ++ 1x의 이동 생성자를 사용하여 이동할 수 있습니다.

unique_ptr<type> p(new type);
unique_ptr<type> q(p); // not legal!
unique_ptr<type> r(move(p)); // legal. p is now empty, but r owns the object
unique_ptr<type> s(function_returning_a_unique_ptr()); // legal!

이것은 std :: auto_ptr이 준수하는 의미론이지만 이동에 대한 기본 지원이 누락되어 함정없이 제공하지 못합니다. unique_ptr은 이동 의미론의 주요 기능 중 하나 인 임시 다른 unique_ptr에서 자동으로 리소스를 훔칩니다. auto_ptr은 unique_ptr을 위해 다음 C ++ 표준 릴리스에서 더 이상 사용되지 않습니다. C ++ 1x는 또한 이동 가능하지만 컨테이너로 복사 할 수없는 스터핑 객체를 허용합니다. 예를 들어 unique_ptr을 벡터에 넣을 수 있습니다. 나는 여기서 멈추고 이것에 대해 더 자세히 읽고 싶다면 이것에 관한 훌륭한 기사 를 참조 할 것 입니다.


3
칭찬에 감사드립니다. 감사합니다. +1도 받고 있습니다 : p
Johannes Schaub-litb

@litb : "소유권 이전"에 의문의 여지가 있습니다. 나는 어떤 존재하지 동의 실제 C ++ 03에서하지만, 스마트 포인터에 대한 객체 사이에 소유권 이전이은으로 할 수없는 파괴적인 복사 여기에 언급 메커니즘 informit.com/articles/article.aspx?p=31529&seqNum= 도 5 .
legends2k

3
환상적인 답변. 참고 : auto_ptr이미 더 이상 사용되지 않습니다 (C ++ 11).
nickolay

2
"이러한 요구 사항은 이러한 스마트 포인터의 소위"이동 생성자 "동작과 호환되지 않는 컨테이너 요소의 복사 생성자의 특정 동작을 요구하기 때문에 컨테이너에서의 사용법이 중단됩니다." 그 부분을 얻지 못했습니다.
Raja

또한 캐시 일관성을 향상시키는 intrusive_ptr것이 바람직 할 수 있다고 들었습니다 shared_ptr. 참조 수를 별도의 개체 대신 관리되는 개체 자체의 메모리의 일부로 저장하면 캐시 성능이 향상됩니다. 이것은 관리 대상 객체의 템플릿 또는 수퍼 클래스로 구현 될 수 있습니다.
Eliot

91

scoped_ptr 이 가장 간단합니다. 범위를 벗어나면 파괴됩니다. 다음 코드는 유효하지 않지만 (scoped_ptr은 복사 할 수 없음) 요점을 보여줍니다.

std::vector< scoped_ptr<T> > tPtrVec;
{
     scoped_ptr<T> tPtr(new T());
     tPtrVec.push_back(tPtr);
     // raw T* is freed
}
tPtrVec[0]->DoSomething(); // accessing freed memory

shared_ptr 은 참조 횟수입니다. 복사 또는 할당이 발생할 때마다 참조 횟수가 증가합니다. 인스턴스의 소멸자가 실행될 때마다 원시 T *에 대한 참조 수가 감소합니다. 0이되면 포인터가 해제됩니다.

std::vector< shared_ptr<T> > tPtrVec;
{
     shared_ptr<T> tPtr(new T());
     // This copy to tPtrVec.push_back and ultimately to the vector storage
     // causes the reference count to go from 1->2
     tPtrVec.push_back(tPtr);
     // num references to T goes from 2->1 on the destruction of tPtr
}
tPtrVec[0]->DoSomething(); // raw T* still exists, so this is safe

weak_ptr 은 공유 포인터에 대한 약한 참조이며, 지적한 shared_ptr이 여전히 주변에 있는지 확인해야합니다.

std::vector< weak_ptr<T> > tPtrVec;
{
     shared_ptr<T> tPtr(new T());
     tPtrVec.push_back(tPtr);
     // num references to T goes from 1->0
}
shared_ptr<T> tPtrAccessed =  tPtrVec[0].lock();
if (tPtrAccessed[0].get() == 0)
{
     cout << "Raw T* was freed, can't access it"
}
else
{
     tPtrVec[0]->DoSomething(); // raw 
}

intrusive_ptr 은 일반적으로 사용해야하는 타사 스마트 ptr이있을 때 사용됩니다. 참조 카운트를 추가 및 감소시키는 무료 함수를 호출합니다 . 자세한 내용은 설명서를 늘리려면 링크 를 참조하십시오.


아닌 if (tPtrAccessed[0].get() == 0)것으로 가정 if (tPtrAccessed.get() == 0) ?
Rajeshwar

@DougT. Java가 References와 동일한 아이디어를 사용한다고 생각하십니까? 연약하고 단단하고 약한 등?
gansub

20

boost::ptr_container부스트 스마트 포인터에 대한 설문 조사를 간과하지 마십시오 . 예를 들어 std::vector<boost::shared_ptr<T> >속도가 너무 느린 상황에서는 가치 가 없습니다.


실제로 지난번 시도했을 때, 벤치마킹은 적어도 전형적인 PC HW에서 처음 작성 한 이후 성능 격차가 크게 줄어들 었다는 것을 보여주었습니다! 보다 효율적인 ptr_container 접근 방식은 여전히 ​​틈새 사용 사례에서 몇 가지 장점을 가질 수 있습니다.
timday

12

나는 문서를 보는 것에 대한 조언을 두 번째. 그것은 보이는 것처럼 무섭지 않습니다. 그리고 몇 가지 짧은 힌트 :

  • scoped_ptr-포인터가 범위를 벗어나면 자동으로 삭제됩니다. 참고-할당은 불가능하지만 오버 헤드는 발생하지 않습니다.
  • intrusive_ptr-오버 헤드가없는 참조 카운팅 포인터 smart_ptr. 그러나 객체 자체는 참조 횟수를 저장합니다
  • weak_ptr- shared_ptr순환 종속성을 초래하는 상황을 처리하기 위해 함께 작동합니다 (문서를 읽고 Google에서 멋진 그림을 검색하십시오).
  • shared_ptr -스마트 포인터의 가장 강력하고 강력한 헤비급 (부스트가 제공하는 것)
  • 또한 auto_ptr컨트롤이 범위를 벗어날 때 객체가 가리키는 객체가 자동으로 파괴되도록하는 old도 있습니다 . 그러나 다른 사람들과는 다른 복사 의미가 있습니다.
  • unique_ptr- C ++ 0x로 올 것이다

편집 할 응답 :


8
부스트 문서가 너무 무서워서 여기에 왔습니다.
Francois Botha
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.