ProgramingTip

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

bestdevel 2020. 11. 1. 18:27
반응형

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


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참조로 입력 된 것을 전달하는 것이 본능적이지 않은 것처럼 보일 것입니다. 둘째, 서명이 더 커지기 때문입니다. 셋째, 생성 된 코드에서 내 변수에 도달 한 두 개의 연속 포인터 간접 지정이 필요하기 때문입니다.


전달하십시오. 어떤 종류의 스마트 포인터로만 작동하는 함수가 없습니다.

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);

사용의 장점은 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은 역 참조 할 수있는 "아무것도"또는 "목적물 없음"의 의미를 가질 수 있습니다. ( 나쁜 )
  3. 프로세스의 주소 지정 가능 공간 밖에있는 메모리 블록의 주소입니다 (해석을 해제하면 프로그램이 중단 될 수 있음). ( 못생긴 )
  4. 역 참조 할 수없는 예상 한 내용을 포함하지 않는 메모리 블록의 주소입니다. 포인터가 실수로 수정되어 이제 다른 쓰기 가능한 주소 (프로세스 삭제 된 다른 변수)를 가리킬 수 있습니다. 이 메모리 위치에 쓰는 것은 허용되는 것이 당신이 거기에 쓰는 것이 허용되는 한 OS가 불평하지 않기 때문에 실행되는 것이 많은 재미를 유발합니다. ( Zoinks! )

일반적으로 스마트 포인터를 사용하면 일반적으로 타임에 감지 할 수없고 일반적으로 프로그램이 충돌하거나 작동하지 못한 일을 수행 할 때 발생하는 경험하는 다소 무서운 사례 3과 4를 예상합니다.

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

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

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

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


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

bool func(BaseClass& base, int other_arg);

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

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


개인적으로 포인터 / 스마트 포인터에서 참조를 가져 오는 것을 피합니다. 포인터가 있으면 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오버 헤드가 보상되지 않습니다.

참고 URL : https://stackoverflow.com/questions/11277249/how-to-pass-stdunique-ptr-around

반응형