스마트 포인터 : 개체 소유자는 누구입니까? [닫은]


114

C ++는 메모리 소유권에 관한 것 입니다. 즉 소유권 의미론 이라고도 합니다.

동적으로 할당 된 메모리 청크의 소유자는 해당 메모리를 해제 할 책임이 있습니다. 따라서 질문은 실제로 누가 메모리를 소유하는지가됩니다.

C ++에서 소유권은 원시 포인터가 내부에 래핑되어 있으므로 좋은 (IMO) C ++ 프로그램에서 원시 포인터가 전달되는 것을 보는 것은 매우 드뭅니다 ( 드물게 , 절대로 아님). (원시 포인터에는 유추 된 소유권이 없으므로 누가 메모리를 소유하고 있는지 알려주지 않으므로 문서를주의 깊게 읽지 않으면 누가 소유권을 가지고 있는지 알 수 없습니다.)

반대로, 각 원시 포인터가 자체 스마트 포인터 래퍼 내에 저장되는 클래스에 저장된 원시 포인터를 보는 경우는 거의 없습니다. ( 주의 : 객체를 소유하지 않은 경우 언제 범위를 벗어나 파괴 될지 알 수 없기 때문에 저장해서는 안됩니다.)

그래서 질문 :

  • 사람들은 어떤 유형의 소유권 의미를 발견 했습니까?
  • 이러한 의미를 구현하는 데 사용되는 표준 클래스는 무엇입니까?
  • 어떤 상황에서 유용하다고 생각합니까?

답변 당 1 가지 유형의 의미 소유권을 유지하여 개별적으로 투표 할 수 있도록합니다.

요약:

개념적으로 스마트 포인터는 간단하고 순진한 구현이 쉽습니다. 나는 많은 시도 된 구현을 보았지만, 일상적인 사용과 예제에는 분명하지 않은 방식으로 항상 깨졌습니다. 따라서 나는 항상 라이브러리에서 잘 테스트 된 스마트 포인터를 사용하는 것이 좋습니다. std::auto_ptr또는 Boost 스마트 포인터 중 하나가 내 모든 요구 사항을 충족하는 것 같습니다.

std::auto_ptr<T>:

한 사람이 개체를 소유합니다. 소유권 이전이 허용됩니다.

사용법 : 소유권의 명시 적 이전을 표시하는 인터페이스를 정의 할 수 있습니다.

boost::scoped_ptr<T>

한 사람이 개체를 소유합니다. 소유권 이전은 허용되지 않습니다.

사용법 : 명시적인 소유권을 표시하는 데 사용됩니다. 개체는 소멸자에 의해 또는 명시 적으로 재설정 될 때 파괴됩니다.

boost::shared_ptr<T>( std::tr1::shared_ptr<T>)

다중 소유권. 이것은 간단한 참조 카운트 포인터입니다. 참조 횟수가 0에 도달하면 개체가 삭제됩니다.

사용법 : 객체가 컴파일 타임에 결정할 수없는 수명을 가진 여러 빚을 가질 수있는 경우.

boost::weak_ptr<T>:

shared_ptr<T>포인터주기가 발생할 수있는 상황에서 와 함께 사용됩니다 .

사용법 : 주기만 공유 참조 카운트를 유지하고있을 때 개체를 유지하는주기를 중지하는 데 사용됩니다.


14
?? 질문은 무엇입니까?
Pacerier

9
난 그냥 unique_ptr이 질문을 게시 한 이후 auto_ptr은가 (지금 위하여 표준화)의 찬성에서 사용되지 지적하고 싶었
후안 CAMPA

In C++ ownership is documented by the type a RAW pointer is wrapped inside thus in a good (IMO) 이것은 다시 표현할 수 있습니까? 나는 그것을 전혀 이해하지 못한다.
lolololol ol dec

@lololololol 문장을 반으로 자릅니다. In C++ ownership is documented by the type a RAW pointer is wrapped inside thus in a good C++ program it is very rare to see RAW pointers passed around. RAW 포인터에는 소유권 의미가 없습니다. 소유자를 모르는 경우 개체 삭제를 담당하는 사람을 알 수 없습니다. 소유권을 정의하는 포인터 (std :: shared_ptr, std :: unique_ptr 등)를 래핑하는 데 사용되는 몇 가지 표준 클래스가 있습니다. 포인터 삭제 책임자를 정의합니다.
Martin York

1
C ++ 11 +에서는 auto_ptr을 사용하지 마십시오! 대신 unique_ptr을 사용하십시오!
발 말한다 Reinstate Monica

답변:


20

저에게는이 3 가지 종류가 저의 대부분의 요구 사항을 충족합니다.

shared_ptr -카운터가 0에 도달하면 참조 카운트, 할당 해제

weak_ptr -위와 동일하지만 '노예'입니다. shared_ptr 이므로 할당을 취소 할 수 없습니다.

auto_ptr-생성 및 할당 해제가 동일한 함수 내에서 발생하거나 객체가 항상 한 명의 소유자로 간주되어야하는 경우. 하나의 포인터를 다른 포인터에 할당하면 두 번째 포인터가 첫 번째 포인터에서 개체를 '훔칩니다'.

이것에 대한 자체 구현이 있지만 Boost .

여전히 참조로 객체를 전달합니다 (const .

내가 사용하는 또 다른 종류의 포인터가 있는데이를 hub_ptr 이라고 부릅니다 . 중첩 된 개체 (일반적으로 가상 기본 클래스로)에서 액세스 할 수 있어야하는 개체가있는 경우입니다. 이것은 weak_ptr그들에게 a 를 전달함으로써 해결할 수 있지만, shared_ptr그 자체로 는 a가 없습니다 . 이 객체가 그보다 오래 살지 않을 것이라는 것을 알고 있으므로 hub_ptr을 전달합니다 (일반 포인터에 대한 템플릿 래퍼 일뿐입니다).


2
자신 만의 포인터 클래스 (hub_ptr)를 만드는 대신 * this를 이러한 객체에 전달하고 참조로 저장하도록하는 것이 어떻습니까? 소유하는 클래스와 동시에 객체가 파괴된다는 사실을 알고 있기 때문에 너무 많은 농구를 뛰어 넘는 요점을 이해하지 못합니다.
Michel

4
기본적으로 명확하게하기위한 디자인 계약입니다. 자식 개체가 hub_ptr을 받으면 뾰족한 개체가 자식의 수명 동안 파괴되지 않으며 소유권이 없음을 알고 있습니다. 포함 된 개체와 컨테이너 개체는 모두 명확한 규칙 집합에 동의합니다. 네이 키드 포인터를 사용하는 경우 규칙을 문서화 할 수 있지만 컴파일러와 코드에 의해 적용되지 않습니다.
Fabio Ceconello

1
또한 릴리스 빌드에서 hub_ptr을 네이 키드 포인터로 typedef하기 위해 #ifdef를 사용할 수 있으므로 오버 헤드는 디버그 빌드에만 존재합니다.
Fabio Ceconello

3
있습니다 부스트 문서는 scoped_ptr를 당신의 설명과 모순된다. 그것은 그것이 noncopyable있고 소유권을 양도 할 수 없다는 것을 나타냅니다.
Alec Thomas

3
@Alec Thomas, 당신 말이 맞아요. auto_ptr에 대해 생각하고 scoped_ptr을 썼습니다. 수정되었습니다.
Fabio Ceconello 2012-06-01

23

간단한 C ++ 모델

대부분의 모듈의 I 톱에서, 기본적으로는 수신 포인터가되었다고 가정 하였다 되지 소유권을 수신하는 단계를 포함한다. 사실, 포인터의 소유권을 포기하는 함수 / 메소드는 매우 드물고 그 사실을 문서에 명시 적으로 표현했습니다.

이 모델은 사용자가 명시 적으로 할당 한 항목의 소유자라고 가정합니다. . 나머지는 모두 자동으로 처리됩니다 (범위 종료시 또는 RAII를 통해). 이것은 대부분의 포인터가 자동으로 또는 필요할 때 (대부분 객체 파괴시) 할당을 해제하는 객체가 소유하고 있으며 객체의 수명이 예측 가능하다는 사실에 의해 확장 된 C와 유사한 모델입니다 (RAII는 친구입니다. 다시).

이 모델에서 원시 포인터는 자유롭게 순환하고 대부분 위험하지 않습니다 (하지만 개발자가 충분히 똑똑하다면 가능할 때마다 참조를 대신 사용합니다).

  • 원시 포인터
  • std :: auto_ptr
  • boost :: scoped_ptr

스마트 포인트 C ++ 모델

스마트 포인터로 가득 찬 코드에서 사용자는 개체의 수명을 무시할 수 있습니다. 소유자는 사용자 코드가 아닙니다. 스마트 포인터 자체입니다 (RAII). 문제는 참조 카운트 스마트 포인터와 혼합 된 순환 참조가 치명적일 수 있으므로 공유 포인터와 약한 포인터를 모두 처리해야한다는 것입니다. 따라서 여전히 고려해야 할 소유권이 있습니다 (약한 포인터는 원시 포인터보다 이점이 있다고 말할 수 있더라도 아무 것도 가리킬 수 없습니다).

  • boost :: shared_ptr
  • boost :: weak_ptr

결론

아니 내가 설명하는 모델 문제 를 제외하지 않는 한, 수신 포인터가되어 있지 소유권을 수신 하고 이 사람을 소유 누구인지 여전히 매우 중요하다 . 참조 및 / 또는 스마트 포인터를 많이 사용하는 C ++ 코드의 경우에도 마찬가지입니다.


10

공유 소유권이 없습니다. 그렇다면 제어하지 않는 코드에만 해당하는지 확인하십시오.

그것은 모든 것이 어떻게 상호 작용하는지 이해하도록 강요하기 때문에 문제의 100 %를 해결합니다.


2
  • 공유 소유권
  • boost :: shared_ptr

리소스가 여러 개체간에 공유되는 경우. 부스트 shared_ptr은 참조 카운트를 사용하여 모든 사람이 확인되었을 때 리소스가 할당 해제되었는지 확인합니다.


2

std::tr1::shared_ptr<Blah> 가장 좋은 방법입니다.


2
shared_ptr이 가장 ​​일반적입니다. 그러나 더 많은 것이 있습니다. 각각 고유 한 사용 패턴과 고소 할 좋은 곳과 나쁜 곳이 있습니다. 조금 더 설명하면 좋을 것입니다.
Martin York

이전 컴파일러를 사용하는 경우 boost :: shared_ptr <blah>는 std :: tr1 :: shared_ptr <blah>의 기반입니다. 컴파일러가 최신 버전의 Boost에서 지원되지 않더라도 Boost에서 추출하여 사용할 수있을만큼 간단한 클래스입니다.
Branan

2

부스트에서 포인터 컨테이너 라이브러리도 있습니다. 컨테이너의 컨텍스트에서만 개체를 ​​사용하는 경우 스마트 포인터의 표준 컨테이너보다 더 효율적이고 사용하기 쉽습니다.

Windows에는 COM 포인터 (IUnknown, IDispatch 및 friends) 와이 를 처리하기위한 다양한 스마트 포인터 (예 : ATL의 CComPtr_com_ptr 클래스를 기반으로하는 Visual Studio의 "import"문에 의해 자동 생성 된 스마트 포인터)가 있습니다. ).


1
  • 한 명의 소유자
  • boost :: scoped_ptr

메모리를 동적으로 할당해야하지만 블록의 모든 종료 지점에서 할당 해제되었는지 확인하려는 경우.

누수에 대해 걱정할 필요없이 쉽게 재 장착 및 해제 할 수있어 유용합니다.


1

나는 내 디자인에서 공동 소유권을 가질 수있는 위치에 있지 않다고 생각한다. 사실, 내 머리 위에서 생각할 수있는 유일한 유효한 경우는 플라이 웨이트 패턴입니다.


1

yasper :: ptr은 대안과 같은 경량의 boost :: shared_ptr입니다. 내 (현재) 작은 프로젝트에서 잘 작동합니다.

http://yasper.sourceforge.net/ 의 웹 페이지에는 다음과 같이 설명되어 있습니다.

다른 C ++ 스마트 포인터를 작성하는 이유는 무엇입니까? 이미 C ++ 용 고품질 스마트 포인터 구현이 여러 개 있으며, 가장 두드러진 것은 Boost 포인터 판테온과 Loki의 SmartPtr입니다. 스마트 포인터 구현의 좋은 비교와 그 사용이 적절한 경우 Herb Sutter의 The New C ++ : Smart (er) Pointers를 읽어보세요. 다른 라이브러리의 확장 기능과는 달리 Yasper는 좁게 초점을 맞춘 참조 계수 포인터입니다. Boost의 shared_ptr 및 Loki의 RefCounted / AllowConversion 정책과 밀접하게 일치합니다. Yasper를 사용하면 C ++ 프로그래머가 Boost의 큰 종속성을 도입하거나 Loki의 복잡한 정책 템플릿에 대해 배울 필요없이 메모리 관리를 잊을 수 있습니다. 철학

* small (contained in single header)
* simple (nothing fancy in the code, easy to understand)
* maximum compatibility (drop in replacement for dumb pointers)

yasper는 다른 구현에서 허용하지 않는 위험한 (아직 유용한) 작업 (예 : 원시 포인터에 대한 할당 및 수동 해제)을 허용하기 때문에 마지막 지점은 위험 할 수 있습니다. 조심하세요. 무엇을하고 있는지 알고있는 경우에만 이러한 기능을 사용하세요!


1

단일 양도 가능 소유자의 또 다른 자주 사용되는 형태가 있으며, 할당 의미론의 미친 손상으로 auto_ptr인한 문제를 방지 하기 때문에 더 auto_ptr좋습니다.

다름 아닌 swap. 적절한 swap기능을 가진 모든 유형을 스마트 참조 로 생각할 수 있습니다. 은 소유권이 동일한 유형의 다른 인스턴스로 전환 될 때까지 소유하는 일부 콘텐츠에 로 . 각 인스턴스는 ID를 유지하지만 새 콘텐츠에 바인딩됩니다. 안전하게 다시 바인딩 할 수있는 참조와 같습니다.

(콘텐츠를 가져 오기 위해 명시 적으로 역 참조 할 필요가 없기 때문에 스마트 포인터가 아니라 스마트 참조입니다.)

이것은 auto_ptr이 덜 필요하다는 것을 의미합니다. 타입이 좋은 swap기능을 가지고 있지 않은 틈을 메우기 위해서만 필요 합니다. 그러나 모든 표준 컨테이너는 그렇습니다.


덜 필요해질 수도 있지만 (scoped_ptr이 이것보다 덜 필요하다고 말하고 싶습니다), 사라지지는 않습니다. 스왑 기능을 갖는 것은 힙에 무언가를 할당하고 삭제하기 전에 누군가가 던지거나 단순히 잊어 버리면 전혀 도움이되지 않습니다.
Michel

이것이 바로 제가 마지막 단락에서 말한 것입니다.
Daniel Earwicker

0
  • 하나의 소유자 : Aka delete on Copy
  • std :: auto_ptr

객체의 작성자가 명시 적으로 다른 사람에게 소유권을 넘기려고 할 때. 이것은 또한 내가 제공하는 코드에 문서화하는 방법이며 더 이상 추적하지 않으므로 완료되면 삭제하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.