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


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.