Programing

shared_ptr을 언제 사용하고 언제 원시 포인터를 사용합니까?

lottogame 2020. 10. 30. 07:38
반응형

shared_ptr을 언제 사용하고 언제 원시 포인터를 사용합니까?


class B;

class A
{
public:
    A ()
        : m_b(new B())
    {
    }

    shared_ptr<B> GimmeB ()
    {
        return m_b;
    }

private:
    shared_ptr<B> m_b;
};

B가 의미 상 A의 수명 외에 존재해서는 안되는 클래스라고 가정 해 보겠습니다. 즉, B가 단독으로 존재하는 것은 전혀 의미가 없습니다. 또는 a를 반환 해야합니까 ?GimmeBshared_ptr<B>B*

일반적으로 스마트 포인터 대신 C ++ 코드에서 원시 포인터를 사용하지 않는 것이 좋은 방법입니까?

나는 shared_ptr명시적인 소유권 이전 또는 공유가있을 때만 사용해야한다고 생각하는데, 함수가 일부 메모리를 할당하고 일부 데이터로 채우고 반환하고 이해하는 경우를 제외하고는 매우 드물다고 생각합니다. 발신자와 수신자 사이에서 전자가 이제 해당 데이터에 대해 "책임을집니다".


당신의 분석은 꽤 정확하다고 생각합니다. 이 상황에서 나는 또한 bare를 반환 B*하거나 [const] B&객체가 절대 null이 될 수없는 경우 에도를 반환합니다.

스마트 포인터를 숙독 할 시간을 갖고 많은 경우에해야 할 일을 알려주는 몇 가지 지침에 도달했습니다.

  • 호출자가 수명을 관리 할 객체를 반환하는 경우 std::unique_ptr. 발신자는 std::shared_ptr원하는 경우에 할당 할 수 있습니다 .
  • 반환 std::shared_ptr은 실제로 매우 드뭅니다. 그럴 경우 일반적으로 분명합니다. 호출자에게 원래 리소스를 유지하던 객체의 수명을 넘어서 가리키는 객체의 수명을 연장 할 것임을 알립니다. 공장에서 공유 포인터를 반환하는 것도 예외는 아닙니다. 예를 들어 이렇게해야합니다. 당신이 사용하는 경우 std::enable_shared_from_this.
  • 방법 std::weak_ptr을 이해하고 싶을 때를 제외하고는 거의 필요하지 않습니다 lock. 이것은 몇 가지 용도가 있지만 드문 경우입니다. 귀하의 예에서 A객체 의 수명이 호출자의 관점에서 결정적이지 않은 경우 이것은 고려해야 할 사항이었습니다.
  • 호출자가 수명을 제어 할 수없는 기존 객체에 대한 참조를 반환하면 베어 포인터 또는 참조를 반환합니다. 이렇게하면 호출자에게 개체가 존재하며 그 수명을 관리 할 필요가 없음을 알립니다. nullptr을 사용하지 않으면 참조를 반환해야 합니다.

"언제 shared_ptr그리고 언제 원시 포인터를 사용해야합니까?"라는 질문 매우 간단한 대답이 있습니다.

  • 포인터에 소유권을 첨부하지 않으려면 원시 포인터를 사용하십시오. 이 작업은 종종 참조로 수행 할 수도 있습니다. 원시 포인터는 일부 저수준 코드 (예 : 스마트 포인터 구현 또는 컨테이너 구현)에서도 사용할 수 있습니다.
  • unique_ptr또는 scope_ptr개체의 고유 한 소유권을 원할 때 사용 합니다. 이것은 가장 유용한 옵션이며 대부분의 경우에 사용해야합니다. 고유 한 소유권은 포인터를 사용하지 않고 객체를 직접 생성하여 표현할 수도 있습니다 (가능하다면를 사용하는 것보다 훨씬 좋습니다 unique_ptr).
  • 포인터의 공유 소유권을 원할 때 shared_ptr또는 사용하십시오 intrusive_ptr. 이것은 혼란스럽고 비효율적 일 수 있으며 종종 좋은 옵션이 아닙니다. 공유 소유권은 일부 복잡한 디자인에서 유용 할 수 있지만 이해하기 어려운 코드로 이어지기 때문에 일반적으로 피해야합니다.

shared_ptrs는 원시 포인터와 완전히 다른 작업을 수행하며 shared_ptrs 또는 원시 포인터는 대부분의 코드에 대해 최상의 옵션이 아닙니다.


다음은 좋은 경험 법칙입니다.

  • 양도가 없거나 공유 된 소유권 참조 또는 일반 포인터가 충분할 때. (일반 포인터는 참조보다 유연합니다.)
  • 소유권 이전이 있지만 공유 소유권 std::unique_ptr<>이없는 경우 좋은 선택입니다. 종종 공장 기능이있는 경우.
  • 공유 소유권이있는 경우 std::shared_ptr<>또는에 대한 좋은 사용 사례입니다 boost::intrusive_ptr<>.

공유 소유권을 피하는 것이 가장 좋습니다. 부분적으로는 복사 측면에서 가장 비싸고 std::shared_ptr<>일반 포인터 저장 공간의 두 배를 차지하기 때문입니다. 그러나 가장 중요한 것은 명확한 소유자가없는 열악한 설계에 도움이되기 때문입니다. 차례로 서로에 대한 공유 포인터를 보유하고 있기 때문에 파괴 할 수없는 개체의 털 덩어리로 이어집니다.

가장 좋은 디자인은 명확한 소유권이 확립되고 계층 적이므로 이상적으로는 스마트 포인터가 전혀 필요하지 않습니다. 예를 들어, 고유 한 객체를 생성하거나 기존 객체를 반환하는 공장이있는 경우, 공장이 생성 한 객체를 소유하고 연관 컨테이너 (예 :)에 값별로 유지하여 일반 객체 std::unordered_map를 반환 할 수 있도록하는 것이 좋습니다. 사용자에 대한 포인터 또는 참조. 이 팩토리는 사용자가 이미 파괴 된 객체에 대한 포인터를 가질 수 없도록 첫 번째 사용자 이전에 시작되고 마지막 사용자 (계층 적 속성) 이후에 끝나는 수명을 가져야합니다.


GimmeB ()의 피 호출자가 A 인스턴스가 종료 된 후 ptr의 복사본을 유지하여 포인터의 수명을 연장 할 수 없도록하려면 shared_ptr을 반환해서는 안됩니다.

피 호출자가 반환 된 포인터를 오랜 기간 동안 유지하지 않아야합니다. 즉, A의 수명이 포인터보다 먼저 만료 될 위험이없는 경우 원시 포인터가 더 좋습니다. 그러나 더 나은 선택은 실제 원시 포인터를 사용할 좋은 이유가없는 한 참조를 사용하는 것입니다.

마지막으로 A 인스턴스의 수명이 만료 된 후에도 반환 된 포인터가 존재할 수 있지만 포인터 자체가 B의 수명을 연장하지 않으려는 경우 weak_ptr을 반환하여 테스트에 사용할 수 있습니다. 여전히 존재하는지 여부.

결론은 일반적으로 원시 포인터를 사용하는 것보다 더 좋은 솔루션이 있다는 것입니다.


shared_ptr자원의 명시 적 공유가 발생할 때 가장 잘 사용되는 귀하의 의견에 동의 하지만 다른 유형의 스마트 포인터를 사용할 수 있습니다.

In your precise case: why not return a reference ?

A pointer suggests that the data might be null, however here there will always be a B in your A, thus it will never be null. The reference asserts this behavior.

That being said, I have seen people advocating the use of shared_ptr even in non-shared environments, and giving weak_ptr handles, with the idea of "securing" the application and avoiding stale pointers. Unfortunately, since you can recover a shared_ptr from the weak_ptr (and it is the only way to actually manipulate the data), this is still shared ownership even if it was not meant to be.

Note: there is a subtle bug with shared_ptr, a copy of A will share the same B as the original by default, unless you explicitly write a copy constructor and a copy assignment operator. And of course you would not use a raw pointer in A to hold a B, would you :) ?


Of course, another question is whether you actually need to do so. One of the tenets of good design is encapsulation. To achieve encapsulation:

You shall not return handles to your internals (see Law of Demeter).

so perhaps the real answer to your question is that instead of giving away a reference or pointer to B, it should only be modified through A's interface.


Generally, I would avoid using raw pointers as far as possible since they have very ambiguous meaning - you might have to deallocate the pointee, but maybe not, and only human-read and -written documentation tells you what the case is. And documentation is always bad, outdated or misunderstood.

If ownership is an issue, use a smart pointer. If not, I'd use a reference if practicable.


  1. You allocate B at constuction of A.
  2. You say B shouldn't persist outside As lifetime.
    Both these point to B being a member of A and a just returning a reference accessor. Are you overengineering this?

I found that the C++ Core Guidelines give some very useful hints for this question:

To use raw pointer(T*) or smarter pointer depends on who owns the object (whose responsibility to release memory of the obj).

own :

smart pointer, owner<T*>

not own:

T*, T&, span<>

owner<>, span<> is defined in Microsoft GSL library

here is the rules of thumb:

1) never use raw pointer(or not own types) to pass ownership

2) smart pointer should only be used when ownership semantics are intended

3) T* or owner designate a individual object(only)

4) use vector/array/span for array

5) To my undetstanding, shared_ptr is usually used when you don't know who will release the obj, for example, one obj is used by multi-thread


It is good practice to avoid using raw pointers, but you can not just replace everything with shared_ptr. In the example, users of your class will assume that it's ok to extend B's lifetime beyond that of A's, and may decide to hold the returned B object for some time for their own reasons. You should return a weak_ptr, or, if B absolutely cannot exist when A is destroyed, a reference to B or simply a raw pointer.


When you say: "Let's say B is a class that semantically should not exist outside of the lifetime of A"

This tells me B should logically not exist without A, but what about physically existing? If you can be sure no one will try using a *B after A dtors than perhaps a raw pointer will be fine. Otherwise a smarter pointer may be appropriate.

When clients have a direct pointer to A you have to trust they'll handle it appropriately; not try dtoring it etc.

참고URL : https://stackoverflow.com/questions/7657718/when-to-use-shared-ptr-and-when-to-use-raw-pointers

반응형