developer tip

참조 또는 값으로 스마트 포인터 (shared_ptr)를 반환하는 방법은 무엇입니까?

copycodes 2020. 9. 19. 11:29
반응형

참조 또는 값으로 스마트 포인터 (shared_ptr)를 반환하는 방법은 무엇입니까?


을 반환하는 메서드가있는 클래스가 있다고 가정 해 보겠습니다 shared_ptr.

참조 또는 가치로 반품 할 경우 가능한 이점과 단점은 무엇입니까?

두 가지 가능한 단서 :

  • 초기 개체 파괴. shared_ptrby (const) 참조를 반환하면 참조 카운터가 증가하지 않으므로 다른 컨텍스트 (예 : 다른 스레드)에서 범위를 벗어날 때 개체가 삭제 될 위험이 있습니다. 이 올바른지? 환경이 단일 스레드 인 경우이 상황도 발생할 수 있습니까?
  • 비용. 가치에 의한 전달은 확실히 무료가 아닙니다. 가능할 때마다 피할 가치가 있습니까?

모든 분에게 감사합니다.


값으로 스마트 포인터를 반환합니다.

말했듯이 참조로 반환하면 참조 횟수가 제대로 증가하지 않아 부적절한 시간에 무언가를 삭제할 위험이 있습니다. 그것만으로도 참조로 반환하지 않을 충분한 이유가 될 것입니다. 인터페이스는 견고해야합니다.

현재 비용 문제는 RVO ( 반환 값 최적화) 덕분에 문제가되고 있으므로 최신 컴파일러에서 증가-증가-감소 시퀀스 또는 이와 유사한 것을 발생시키지 않을 것입니다. 따라서 a를 반환하는 가장 좋은 방법 shared_ptr은 단순히 값으로 반환하는 것입니다.

shared_ptr<T> Foo()
{
    return shared_ptr<T>(/* acquire something */);
};

이것은 최신 C ++ 컴파일러에 대한 분명한 RVO 기회입니다. Visual C ++ 컴파일러는 모든 최적화가 꺼져 있어도 RVO를 구현한다는 사실을 알고 있습니다. 그리고 C ++ 11의 이동 의미론을 사용하면이 문제는 훨씬 덜 관련성이 있습니다. (하지만 확실한 방법은 프로필을 작성하고 실험하는 것뿐입니다.)

여전히 확신이 서지 않는다면, Dave Abrahams는 가치 반환에 대한 논쟁을 하는 기사가지고 있습니다. 여기에 스 니펫을 재현합니다. 나는 당신이 전체 기사를 읽는 것이 좋습니다.

솔직히 말해서 다음 코드가 기분이 어떤가요?

std::vector<std::string> get_names();
...
std::vector<std::string> const names = get_names();

솔직히 더 잘 알아야하는데 긴장이된다. 원칙적으로, 경우에 get_names()반환, 우리가 가지고 복사 vectorstring들. 그런 다음 초기화 할 때 다시 복사 names해야하고 첫 번째 복사본을 삭제해야합니다. string벡터에 N이있는 경우 각 복사본에는 문자열 내용이 복사 될 때마다 N + 1 메모리 할당과 캐시에 비 친화적 인 데이터 액세스가 많이 필요할 수 있습니다.

그런 종류의 불안에 맞서기보다는 불필요한 사본을 피하기 위해 참조에 의한 전달을 자주 사용합니다.

get_names(std::vector<std::string>& out_param );
...
std::vector<std::string> names;
get_names( names );

불행히도이 접근 방식은 이상적이지 않습니다.

  • 코드가 150 % 증가했습니다.
  • 우리는 const이름을 변경하기 때문에 -ness 를 떨어 뜨려야했습니다.
  • 함수형 프로그래머가 우리에게 상기시키고 싶어하는 것처럼, 돌연변이는 참조 투명성과 방정식 추론을 약화시켜 코드를 추론하기 더 복잡하게 만듭니다.
  • 더 이상 이름에 대한 엄격한 값 의미 체계가 없습니다.

그러나 효율성을 얻기 위해 이런 식으로 코드를 엉망으로 만드는 것이 정말로 필요합니까? 다행히도 대답은 '아니오'로 판명되었습니다 (특히 C ++ 0x를 사용하는 경우에는 그렇지 않습니다).


어떤 스마트 포인터 (shared_ptr뿐만 아니라) 에 관해서도 참조를 반환하는 것이 허용되지 않는다고 생각하며 참조 또는 원시 포인터로 전달하는 것을 매우 주저합니다. 왜? 나중에 참조를 통해 얕은 복사되지 않을지 확신 할 수 없기 때문입니다. 첫 번째 요점은 이것이 우려되어야하는 이유를 정의합니다. 이는 단일 스레드 환경에서도 발생할 수 있습니다. 프로그램에 잘못된 복사본 의미를 적용하기 위해 데이터에 대한 동시 액세스가 필요하지 않습니다. 포인터를 전달한 후에는 사용자가 포인터로 수행하는 작업을 실제로 제어 할 수 없으므로 API 사용자에게 충분한 로프를 제공하는 오용을 조장하지 마십시오.

Secondly, look at your smart pointer's implementation, if possible. Construction and destruction should be darn close to negligible. If this overhead isn't acceptable, then don't use a smart pointer! But beyond this, you will also need to examine the concurrency architecture that you've got, because mutually exclusive access to the mechanism that tracks the uses of the pointer is going to slow you down more than mere construction of the shared_ptr object.

Edit, 3 years later: with the advent of the more modern features in C++, I would tweak my answer to be more accepting of cases when you've simply written a lambda that never lives outside of the calling function's scope, and isn't copied somewhere else. Here, if you wanted to save the very minimal overhead of copying a shared pointer, it would be fair and safe. Why? Because you can guarantee that the reference will never be mis-used.

참고URL : https://stackoverflow.com/questions/10643563/how-to-return-smart-pointers-shared-ptr-by-reference-or-by-value

반응형