std :: unique_ptr을 전달하는 방법은 무엇입니까?


83

C ++ 11을 처음 사용하려고합니다 unique_ptr. 한 클래스가 소유하고 있지만 꽤 자주 전달되는 내 프로젝트 내에서 다형성 원시 포인터를 대체하고 있습니다.

나는 다음과 같은 기능을 사용했습니다.

bool func(BaseClass* ptr, int other_arg) {
  bool val;
  // plain ordinary function that does something...
  return val;
}

하지만 곧 다음으로 전환 할 수 없다는 것을 깨달았습니다.

bool func(std::unique_ptr<BaseClass> ptr, int other_arg);

호출자가 함수에 대한 포인터 소유권을 처리해야하기 때문에 내가 원하지 않는 것입니다. 그렇다면 내 문제에 대한 최선의 해결책은 무엇입니까?

다음과 같이 포인터를 참조로 전달합니다.

bool func(const std::unique_ptr<BaseClass>& ptr, int other_arg);

그러나 저는 그렇게하는 것이 매우 불편합니다. 첫째는 이미 _ptr참조로 입력 된 것을 전달하는 것이 본능적이지 않은 것처럼 보이므로 참조의 참조가 될 것입니다. 둘째, 함수 서명이 더 커지기 때문입니다. 셋째, 생성 된 코드에서 내 변수에 도달하려면 두 개의 연속 포인터 간접 지정이 필요하기 때문입니다.

답변:


97

함수가 pointee를 사용하도록하려면 참조를 전달하십시오. 어떤 종류의 스마트 포인터로만 작동하도록 함수를 연결할 이유가 없습니다.

bool func(BaseClass& base, int other_arg);

그리고 호출 사이트에서 사용 operator*:

func(*some_unique_ptr, 42);

또는 base인수가 null이 될 수있는 경우 서명을 그대로 유지하고 get()멤버 함수를 사용합니다 .

bool func(BaseClass* base, int other_arg);
func(some_unique_ptr.get(), 42);

4
이건 좋지만 캠 std::unique_ptrstd::vector<std::unique_ptr>논쟁 을 위해 없애 버려 ?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

31

사용의 장점은 std::unique_ptr<T>(호출 delete하거나 delete[]명시 적으로 기억할 필요가 없다는 점을 제외하고 ) 포인터가 nullptr(기본) 개체의 유효한 인스턴스 중 하나 이거나 가리키는 것을 보장한다는 것 입니다. 나는 귀하의 질문에 대답 후 나는이 다시 올 것이다,하지만 첫 번째 메시지는 DO가 동적으로 할당 된 개체의 수명을 관리하는 스마트 포인터를 사용합니다.

이제 문제 는 실제로 이전 코드에서 이것을 사용하는 방법 입니다.

내 제안은 소유권을 이전하거나 공유하지 않으려면 항상 객체에 대한 참조를 전달 해야한다는 것 입니다. 다음과 같이 함수를 선언하십시오 ( const필요에 따라 한정자 가 있거나 없음 ).

bool func(BaseClass& ref, int other_arg) { ... }

그런 다음 std::shared_ptr<BaseClass> ptr의지 가있는 호출자 는 nullptr케이스 를 처리 하거나 bool func(...)결과 계산 을 요청 합니다.

if (ptr) {
  result = func(*ptr, some_int);
} else {
  /* the object was, for some reason, either not created or destroyed */
}

이는 모든 호출자가 참조가 유효하며 함수 본문 실행 내내 계속 유효하다는 것을 약속 해야 함을 의미합니다 .


여기에 내가 강력하게한다고 생각하는 이유입니다 하지 스마트 포인터 원시 포인터 또는 참조를 전달합니다.

원시 포인터는 메모리 주소 일뿐입니다. (적어도) 4 가지 의미 중 하나를 가질 수 있습니다.

  1. 원하는 개체가있는 메모리 블록의 주소입니다. ( 좋은 )
  2. 확신 할 수있는 주소 0x0은 역 참조 할 수 없으며 "nothing"또는 "no object"의 의미를 가질 수 있습니다. ( 나쁜 )
  3. 프로세스의 주소 지정 가능 공간 밖에있는 메모리 블록의 주소입니다 (해석을 해제하면 프로그램이 중단 될 수 있습니다). ( 못생긴 )
  4. 역 참조 할 수 있지만 예상 한 내용을 포함하지 않는 메모리 블록의 주소입니다. 포인터가 실수로 수정되어 이제 다른 쓰기 가능한 주소 (프로세스 내의 완전히 다른 변수)를 가리킬 수 있습니다. 이 메모리 위치에 쓰는 것은 당신이 거기에 쓸 수있는 한 OS가 불평하지 않기 때문에 실행 중에 때때로 많은 재미를 유발할 것입니다. ( Zoinks! )

스마트 포인터를 올바르게 사용하면 일반적으로 컴파일 타임에 감지 할 수없고 일반적으로 프로그램이 충돌하거나 예상치 못한 일을 수행 할 때 런타임에만 경험하는 다소 무서운 사례 3과 4를 완화합니다.

스마트 포인터를 인수로 전달하는 const데는 두 가지 단점이 있습니다 . 복사본을 만들지 않고 는 뾰족한 개체 의 -ness를 변경할 수 없으며 (에 대해 오버 헤드를 추가 shared_ptr하고 가능하지 않음 unique_ptr) 여전히 두 번째 ( nullptr) 의미 가 남아 있습니다.

디자인 관점에서 두 번째 사례를 ( 나쁜 ) 으로 표시했습니다 . 이것은 책임에 대한 더 미묘한 주장입니다.

함수 nullptr가 매개 변수로 a 를 수신 할 때 의미하는 바를 상상해보십시오 . 먼저 무엇을할지 결정해야합니다. 누락 된 개체 대신 "마법적인"값을 사용합니까? 동작을 완전히 변경하고 다른 것을 계산합니까 (객체가 필요하지 않음)? 당황하고 예외를 던지시겠습니까? 또한 함수가 원시 포인터로 2 개 또는 3 개 이상의 인수를 취하면 어떻게됩니까? 각각을 확인하고 그에 따라 동작을 조정해야합니다. 이것은 실제 이유없이 입력 유효성 검사에 완전히 새로운 수준을 추가합니다.

발신자는 이러한 결정을 내릴 수있는 충분한 상황 정보를 가진 사람이어야합니다. 즉, 더 많이 알수록 나쁜 것은 덜 두렵습니다. 반면에이 함수는 가리키는 메모리가 의도 한대로 작업하기에 안전하다는 호출자의 약속을 받아 들여야합니다. (참조는 여전히 메모리 주소이지만 개념적으로 유효성에 대한 약속을 나타냅니다.)


25

Martinho의 의견에 동의하지만 참조에 의한 전달의 소유권 의미를 지적하는 것이 중요하다고 생각합니다. 올바른 해결책은 여기에 간단한 참조를 사용하는 것입니다.

bool func(BaseClass& base, int other_arg);

C ++에서 참조에 의한 전달의 일반적으로 허용되는 의미는 함수 호출자가 함수에 "여기서이 객체를 빌려 사용하고 수정할 수 있습니다 (const가 아닌 경우)"라고 말하는 것과 같습니다. 기능 본문의 기간. " 이것은 unique_ptr객체가 단지 짧은 기간 동안 차용되기 때문에 소유권 규칙과 충돌 하지 않습니다. 실제 소유권 이전이 일어나지 않습니다 (차를 누군가에게 빌려 주면 소유권에 서명합니까? 그에게?).

따라서에서 참조 (또는 원시 포인터)를 가져 오는 것이 나쁘게 보일 수 있지만 (디자인 측면에서, 코딩 방법 등)에서 unique_ptr설정 한 소유권 규칙을 완벽하게 따르기 때문이 아닙니다. unique_ptr. 그리고 물론 깔끔한 구문,에서 소유 한 객체에만 제한이없는 것과 같은 다른 좋은 이점 unique_ptr이 있습니다.


0

개인적으로 포인터 / 스마트 포인터에서 참조를 가져 오는 것을 피합니다. 포인터가 있으면 nullptr어떻게 되나요? 서명을 다음과 같이 변경하는 경우 :

bool func(BaseClass& base, int other_arg);

널 포인터 역 참조로부터 코드를 보호해야 할 수도 있습니다.

if (the_unique_ptr)
  func(*the_unique_ptr, 10);

클래스가 포인터의 유일한 소유자 인 경우 Martinho의 두 번째 대안이 더 합리적으로 보입니다.

func(the_unique_ptr.get(), 10);

또는 std::shared_ptr. 그러나를 담당하는 단일 엔티티가있는 delete경우 std::shared_ptr오버 헤드는 보상되지 않습니다.


대부분의 std::unique_ptr경우 오버 헤드가 전혀 없다는 것을 알고 있습니까?
lvella

1
네, 알고 있습니다. 그것이 내가 그에게 std::shared_ptr오버 헤드 에 대해 말한 이유 입니다.
Guilherme Ferreira
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.