최후의 수단으로 std :: shared_ptr?


59

방금 "Going Native 2012"스트림을보고 있었고에 대한 토론을 발견했습니다 std::shared_ptr. 나는 Bjarne의 다소 부정적인 견해 std::shared_ptr와 물체의 수명이 불확실 할 때 "최후의 수단"으로 사용해야한다는 그의 의견 을 듣고 약간 놀랐습니다 .

누구든지 이것을 좀 더 깊이 설명하고 싶습니까? 안전한 방법으로 std::shared_ptr객체 수명을 프로그래밍하지 않고 계속 관리 할 수있는 방법은 무엇입니까?


8
포인터를 사용하지 않습니까? 수명을 관리하는 객체의 고유 한 소유자가 있습니까?
Bo Persson

2
명시 적으로 공유 된 데이터는 어떻습니까? 포인터를 사용하지 않습니다. 또한 표준 : shared_pointer이 더러운 경우에 "평생 관리"할 것
카밀 Klimek

6
제시된 조언에 귀를 기울이고 그 조언에 대한 논쟁에 더 귀를 기울이는 것을 고려 했습니까? 그는 이런 종류의 조언이 작동하는 시스템의 종류를 잘 설명합니다.
Nicol Bolas

@NicolBolas : 나는 조언과 주장을 들었지만 분명히 나는 ​​그것을 충분히 이해했다고 생각하지 않았습니다.
ronag

그는 "최종 리조트"라고 몇시에 말합니까? ( channel9.msdn.com/Events/GoingNative/GoingNative-2012/… ) 에서 36 분에 비트를 보면서 포인터 사용에주의를 기울이고 있지만 일반적으로 shared_ptr 및 unique_ptr뿐만 아니라 ' 일반 '포인터. 그는 객체 자체 (그리고 new로 할당 된 객체에 대한 포인터가 아님)가 선호되어야 함을 의미합니다. 프리젠 테이션에서 나중에 생각했던 비트입니까?
Pharap

답변:


55

공유 소유권을 피할 수 있으면 응용 프로그램이 더 단순하고 이해하기 쉬워 지므로 유지 관리 중에 발생하는 버그에 덜 취약합니다. 복잡하거나 불명확 한 소유권 모델은 쉽게 추적 할 수없는 공유 상태를 통해 응용 프로그램의 여러 부분을 연결하기 어려워하는 경향이 있습니다.

이를 감안할 때 자동 저장 기간이있는 개체를 사용하고 "값"하위 개체를 갖는 것이 좋습니다. 이것을 실패하면, 최후의 수단이 아니더라도 바람직한 도구 목록을 얻는 unique_ptr좋은 대안 이 될 수 있습니다 shared_ptr.


5
문제는 테크노 자체 (공유 소유권)하지만 대한 소개 어려움이없는 것을 poiting +1 우리가 다음에 무슨 일이 일어나고 있는지 해독 할 필요가 단순한 인간.
Matthieu M.

그러나 이러한 접근 방식을 사용하면 대부분의 사소한 OOP 클래스 (비 복사 성으로 인해)에 동시성 프로그래밍 패턴을 적용하는 프로그래머의 기능이 심각하게 제한됩니다.이 문제는 "Going Native 2013"에서 발생합니다.
rwong

48

Bjarne이 사는 세상은 더 나은 학기를 원하기 때문에 매우 학문적입니다. 객체가 매우 고의적 인 관계형 계층 구조를 가지도록 소유권을 관계가 엄격하고 고르지 않게 코드를 설계하고 구조화 할 수있는 경우 코드가 한 방향 (고수준에서 저수준으로)으로 흐르고 개체는 계층 구조를 사용할 필요가 없습니다 shared_ptr. 누군가 규칙을 어겨 야하는 드문 경우에 사용하는 것입니다. 그러나 그렇지 않으면 vector가치 의미론을 사용하는 모든 데이터 구조 또는 기타 데이터 구조에 모든 것을 집어 넣을 수 있습니다 unique_ptr.

그것이 살기 좋은 세상이지만, 항상 그렇게하는 것은 아닙니다. 그러한 방식으로 코드를 구성 할 없다면 , 만들려는 시스템의 설계가 불가능하거나 매우 불쾌한 것이므로 객체의 공유 소유권이 점점 더 많이 필요하다는 것을 알게 될 것입니다 .

그러한 시스템에서, 알몸의 포인터를 잡는 것은 정확히 위험하지는 않지만 질문을 제기합니다. 가장 좋은 점은 객체의 수명에 대해 합리적인 구문 보장을 shared_ptr제공한다는 것 입니다. 깨질 수 있습니까? 물론이야. 그러나 사람들은 또한 일을 할 수 있습니다 . 기본 관리 및 먹이는 소유권을 공유해야하는 할당 된 개체에 적절한 삶의 질을 제공해야합니다.const_castshared_ptr

그런 다음 weak_ptrs가 있으며 shared_ptr. 가 없으면 사용할 수 없습니다 . 시스템이 견고하게 구성되어 있으면 응용 프로그램의 구조가 객체가 당신보다 오래 지속되도록 보장한다는 사실을 알고 있으므로 일부 객체에 대한 알몸 포인터를 저장할 수 있습니다. 내부 또는 외부 값 (예 : X라는 개체 찾기)에 대한 포인터를 반환하는 함수를 호출 할 수 있습니다. 올바르게 구조화 된 코드에서 객체의 수명이 자신의 수명을 초과 한 경우에만 해당 기능을 사용할 수 있습니다. 따라서 객체에 알몸 포인터를 저장하는 것이 좋습니다.

실제 시스템에서는 이러한 강성이 항상 가능한 것은 아니므로 수명 을 합리적으로 보장 할 수있는 방법이 필요 합니다. 때로는 완전한 소유권이 필요하지 않습니다. 때로는 포인터가 나쁜지 또는 좋은지 알 수 있어야합니다. 그건 어디 weak_ptr에서 오는 나는 경우가 있었다. 사용 된 한 unique_ptr또는 boost::scoped_ptr,하지만 난을 사용했다 shared_ptr나는 때문에 특별히 누군가에게 "휘발성"포인터를 제공 할 필요가 있었다. 수명이 불분명 한 포인터는 포인터가 파괴되었을 때 쿼리 할 수 ​​있습니다.

세상의 상태가 불확실 할 때 살아남는 안전한 방법.

를 통해 대신 포인터를 얻기 위해 함수 호출로 수행 할 수 weak_ptr있습니까? 예,하지만 더 쉽게 깨질 수 있습니다. 알몸 포인터를 반환하는 함수는 사용자가 포인터를 장기 저장하는 것과 같은 것을하지 않는다는 것을 구문 적으로 제안 할 방법이 없습니다. a shared_ptr를 반납하면 다른 사람이 간단히 보관하여 물건의 수명을 연장 할 수 있습니다. weak_ptr그러나을 돌려주는 것은 shared_ptr당신이 얻는 것을 저장하는 lock것이 ... 모호한 생각이라는 것을 강력히 제안합니다 . 그것은 당신이 그것을하는 것을 멈추지 않을 것이지만 C ++의 어떤 것도 당신이 코드를 깨는 것을 막을 수 없습니다. weak_ptr자연스럽게하는 일에 대한 저항을 최소화합니다.

그것은 과용shared_ptr 될 수 없다고 말하는 것이 아닙니다 . 확실히 할 수 있습니다. 특히 사전 에 RAII 포인터를 전달하거나 목록에 넣어야했기 때문에 방금 사용한 경우가 많았 습니다. 및 이동 의미하지 않고 , 유일한 진짜 해결책이었다.unique_ptrboost::shared_ptrunique_ptrboost::shared_ptr

그리고 불필요한 곳에서 사용할 수 있습니다. 위에서 언급했듯이, 적절한 코드 구조는의 일부 사용 필요성을 제거 할 수 있습니다 shared_ptr. 그러나 시스템을 이와 같이 구성 할 수없고 여전히 필요한 작업을 수행하는 경우 shared_ptr크게 사용됩니다.


4
+1 : 예를 들어 boost :: asio를보십시오. 나는 아이디어가 많은 영역으로 확장된다고 생각한다. 컴파일 타임에 어떤 UI 위젯 또는 비동기 호출이 객체를 포기 했는지 마지막 으로 알지 못할 수도 있으며 shared_ptr을 사용하면 알 필요 가 없다 . 분명히 모든 상황에 적용되는 것은 아니며 도구 상자의 다른 (매우 유용한) 도구에만 적용됩니다 .
Guy Sirton

3
약간 늦게 언급; shared_ptrC ++이 파이썬과 같은 스크립팅 언어와 통합 된 시스템에 적합합니다. 를 사용 boost::python하면 c ++ 및 python 측의 참조 횟수가 크게 협력합니다. C ++의 모든 객체는 여전히 python으로 보유 할 수 있으며 죽지 않습니다.
eudoxos 2018 년

1
참고로 WebKit이나 Chromium을 사용하지 않습니다 shared_ptr. 둘 다 자체 구현을 사용 intrusive_ptr합니다. 그들은 C ++로 작성된 많은 응용 프로그램을 모두 실제 예입니다 때문에 해당를 가져
G 맨

1
@ gman : Stroustrup의 반대 의견이 shared_ptr동일하게 적용 되기 때문에 귀하의 의견이 매우 오해의 소지가 있음을 알았 습니다 intrusive_ptr: 그는 개념의 특정 철자가 아닌 공유 소유권의 전체 개념에 반대하고 있습니다. 그래서이 질문의 목적을 위해, 그 큰 응용 프로그램이 실제 예 않는 사용은 shared_ptr. (그리고 더 shared_ptr나아가서 , 그것들 은 그것이 가능하지 않은 경우에도 유용 하다는 것을 보여줍니다 weak_ptr.)
ruakh

1
FWIW, Bjarne이 학계에 살고 있다는 주장에 맞서기 위해 : 순수하게 모든 산업 경력 (G20 증권 거래소 공동 설계 및 500K 플레이어 MOG 설계 포함)에서 우리가 정말로 필요했을 때 3 건 만 보았습니다. 공유 소유권. 나는 Bjarne과 200 %입니다.
No-Bugs Hare

37

내가 사용한 적이 없다고 생각 std::shared_ptr합니다.

대부분의 경우 개체는 전체 컬렉션에 속하는 일부 컬렉션과 연결됩니다. 어떤 경우에는 그냥 사용할 수 있습니다 whatever_collection<o_type>또는 whatever_collection<std::unique_ptr<o_type>>컬렉션 객체의 멤버 또는 자동 변수가되고 있음. 물론 동적 객체 수가 필요하지 않은 경우 고정 크기의 자동 배열을 사용할 수 있습니다.

컬렉션을 통한 반복이나 객체의 다른 작업을 수행 할 때 소유권을 공유하는 도우미 기능이 필요하지 않습니다 . 객체를 사용한 다음 반환하면 호출자는 객체가 전체 호출에 대해 활성 상태를 유지하도록 보장합니다 . 이것은 발신자와 수신자 사이에서 가장 많이 사용되는 계약입니다.


Nicol Bolas는 "일부 개체가 알몸의 포인터를 잡고 있으면 개체가 죽으면 ... "객체는 그 객체가 그 객체의 수명 동안 살아남도록 shared_ptr해야합니다.

나는 그 주장을 사지 않습니다. 적어도이 shared_ptr문제는 해결 되지 않습니다 . 는 어때:

  • 일부 해시 테이블이 객체를 보유하고 해당 객체의 해시 코드가 변경되면 ...
  • 일부 함수가 벡터를 반복하고 요소가 해당 벡터에 삽입되면 ...

가비지 수집과 마찬가지로 기본 사용은 shared_ptr프로그래머가 객체 간 또는 함수와 호출자 간의 계약에 대해 생각하지 않도록합니다. 올바른 전제 조건과 사후 조건에 대한 생각이 필요하며 객체 수명은 그보다 큰 파이의 작은 조각 일뿐입니다.

객체는 "죽어"있지 않으며, 일부 코드 조각은 객체를 파괴합니다. 그리고 shared_ptr통화 계약을 파악하는 대신 문제를 던지는 것은 잘못된 안전입니다.


17
@ronag : "원시 포인터가 나쁘기 때문에"원 포인터가 더 나은 곳에서 사용하기 시작한 것 같습니다. 그러나 원시 포인터는 나쁘지 않습니다 . 객체에 대한 첫 번째 소유 포인터를 원시 포인터로 만드는 것만으로 는 좋지 않습니다. 메모리가 수동으로 관리되어야하기 때문에 예외가있는 경우 사소하지 않습니다. 그러나 원시 포인터를 핸들 또는 반복자로 사용하는 것이 좋습니다.
Ben Voigt

4
@ BenVoigt : 물론, 알몸 포인터를 통과하는 데 어려움은 객체의 수명을 모른다는 것입니다. 어떤 물체가 알몸의 포인터를 잡으면 그 물체가 죽으면 ... 즉 정확히 일의 종류 shared_ptrweak_ptr방지하기 위해 설계되었습니다. Bjarne은 세상에서 살려고 노력하는 것은 모든 것이 멋지고 명백한 수명을 지니고 있었고 모든 것이 그 주위에 세워졌습니다. 그리고 그 세상을 만들 수 있다면 좋습니다. 그러나 그것이 현실 세계에서는 그렇지 않습니다. 객체는 필요 있도록 객체가 해당 개체의 인생을 살고있다. 만 shared_ptr그렇게 할 수 있습니다.
Nicol Bolas

5
@NicolBolas : 그건 잘못된 안전입니다. 함수 호출자가 일반적인 보증을 제공하지 않는 경우 : "이 함수는 함수 호출 중에 외부 당사자가이 오브젝트를 건드리지 않습니다"는 어떤 유형의 외부 수정이 허용되는지에 동의해야합니다. shared_ptr하나의 특정 외부 수정 만 완화하고 가장 일반적인 수정은 완화하지 않습니다. 그리고 함수 호출 계약이 달리 명시한 경우, 수명이 올바른지 확인하는 것은 객체의 책임이 아닙니다.
Ben Voigt

6
@NicolBolas : 함수가 객체를 생성하고 포인터로 반환하면 객체에 대한 포인터 unique_ptr가 하나만 있고 소유권 이 있음을 나타내는이어야합니다 .
벤 Voigt

6
@Nicol : 일부 콜렉션에서 포인터를 찾는 경우 해당 콜렉션에있는 포인터 유형을 사용하거나 콜렉션에 값이있는 경우 원시 포인터를 사용해야합니다. 객체를 생성 shared_ptr중이고 호출자가를 원하면 여전히을 반환해야합니다 unique_ptr. 에서 변환 unique_ptr하려면 shared_ptr간단하지만 반대는 논리적으로 불가능하다.
벤 Voigt

16

나는 "마지막 수단"과 같은 절대적인 용어로 생각하지 않고 문제 영역과 관련하여 생각합니다.

C ++는 수명을 관리하는 다양한 방법을 제공 할 수 있습니다. 그들 중 일부는 스택 중심 방식으로 객체를 재구성하려고 시도합니다. 다른 사람들은이 제한을 피하려고합니다. 그들 중 일부는 "literal"이고 다른 일부는 근사치입니다.

실제로 당신은 할 수 있습니다 :

  1. 순수한 가치 의미론을 사용하십시오 . 중요한 것은 "정체성"이 아닌 "값"인 상대적으로 작은 객체에 Person적용 name됩니다. 동일한 것을 가진 두 사람 이 같은 사람 이라고 가정 할 수 있습니다 (더 나은 사람 은 두 사람을 나타냄). 평생은 기계 스택에 의해 부여되며, 본질적으로 프로그램에 중요하지 않습니다 ( 사람이름 이 무엇이든간에 상관없이 Person)
  2. 스택 할당 객체 및 관련 참조 또는 포인터 사용 : 다형성을 허용하고 객체 수명을 부여합니다. "스마트 포인터"가 필요하지 않습니다. 개체가 가리키는 개체보다 스택에 오래 남아있는 구조로 개체를 "지정"할 수 없기 때문입니다 (먼저 개체를 만든 다음 참조하는 구조체).
  3. 스택 관리 힙 할당 객체를 사용하십시오 : 이것은 std :: vector 및 모든 컨테이너가하는 일이며 wat std::unique_ptr는 수행합니다 (크기 1의 벡터로 생각할 수 있습니다). 다시, 객체가 참조하는 데이터 구조 이전 (후)에 객체가 존재하기 시작하고 존재를 종료한다는 것을 인정합니다.

이 mehtod의 약점은 생성 된 위치와 관련하여 더 깊은 스택 수준 호출을 실행하는 동안 개체 유형과 수량이 달라질 수 없다는 것입니다. 객체의 생성 및 삭제가 사용자 활동의 결과 인 모든 상황에서이 모든 기술의 강도가 "실패"하므로 객체의 런타임 유형이 컴파일 타임으로 알려지지 않고 객체를 참조하는 구조가 초과 될 수 있습니다. 사용자는 더 깊은 스택 수준 함수 호출에서 제거하도록 요청합니다. 이 경우 다음 중 하나를 수행해야합니다.

  • 객체 관리 및 관련 참조 구조에 대한 훈련을 소개하거나 ...
  • "순수한 스택 기반 수명 탈출"의 어두운면으로 가십시오. 객체는 생성 한 함수와 독립적으로 남겨야합니다. 그리고 그들이 필요할 때까지 떠나야 합니다 .

C ++ isteslf에는 해당 이벤트 ( while(are_they_needed)) 를 모니터링하는 기본 메커니즘이 없으므로 다음과 같이 근사해야합니다.

  1. 공유 소유권 사용 : 개체 수명이 "참조 카운터"에 바인딩됩니다. "소유권"을 계층 적으로 구성 할 수 있으면 소유권 루프가 존재할 수있는 곳에서 작동합니다. 이것이 std :: shared_ptr이하는 일입니다. 그리고 weak_ptr을 사용하여 루프를 끊을 수 있습니다. 이것은 대부분의 시간에 작동하지만 많은 디자인이 다른 팀에서 일하고 누가 어떤 것을 소유 해야하는지에 대한 명확한 이유가 없습니다 (일반적인 예는 이중 좋아하는 체인입니다 : 이전 다음 또는 이전을 참조하여 이전 또는 다음을 참조하여 이전을 참조해야합니까? 요구 사항이없는 경우이 솔루션은 동일하며 대규모 프로젝트에서는 혼합 할 위험이 있습니다)
  2. 가비지 수집 힙 사용 : 단순히 수명에 신경 쓰지 않습니다. 당신은 때때로 수집가를 운영하고 도달 할 수없는 것은 "더 이상 필요하지 않은"것으로 간주됩니다. 최종? 겨울 왕국?. 많은 GC 수집기가 있지만 실제로 C ++을 인식하는 것을 찾지 못했습니다. 대부분은 객체 파괴에 신경 쓰지 않고 메모리를 비 웁니다.
  3. 적절한 표준 메소드 인터페이스와 함께 C ++ 인식 가비지 수집기를 사용하십시오 . 그것을 찾아 행운을 빈다.

마지막 솔루션에 대한 첫 번째 솔루션으로 이동하면 개체 수명을 관리하고 유지 관리하는 데 소요되는 시간이 지남에 따라 개체 수명을 관리하는 데 필요한 보조 데이터 구조의 양이 증가합니다.

가비지 콜렉터에는 비용이 있고 shared_ptr은 적고 unique_ptr은 훨씬 적으며 스택 관리 오브젝트는 거의 없습니다.

shared_ptr"마지막 리조트는"? 마지막 방법은 가비지 수집기입니다. shared_ptr실제로 std::제안 된 최후의 수단입니다. 그러나 내가 설명 한 상황에 처한 경우 어려운 해결책 일 수 있습니다.


9

나중 세션에서 Herb Sutter가 언급 한 것 중 하나는 복사 할 때마다 shared_ptr<>연동 증가 / 감소가 발생한다는 것입니다. 멀티 코어 시스템의 멀티 스레드 코드에서 메모리 동기화는 중요하지 않습니다. 선택이 주어지면 스택 값을 사용하거나 unique_ptr<>참조 또는 원시 포인터를 전달 하는 것이 좋습니다 .


1
또는 shared_ptrlvalue 또는 rvalue 참조로 전달 하십시오.
ronag

8
요점은 shared_ptr표준에 있기 때문에 모든 메모리 누수 문제를 해결하는 은색 총알처럼 사용하지 마십시오 . 유혹적인 함정이지만 여전히 리소스 소유권을 알고 있어야하며, 소유권을 공유 shared_ptr<>하지 않는 한 최선의 선택은 아닙니다.
Eclipse

나에게 이것은 가장 중요한 세부 사항입니다. 조기 최적화를 참조하십시오. 대부분의 경우이 결정을 내리지 않아야합니다.
Guy Sirton

1
@gbjbaanb : 그렇습니다. 그들은 CPU 수준에 있지만 멀티 코어 시스템에서는 캐시를 무효화하고 메모리 장벽을 강요합니다.
Eclipse

4
내가 작업 한 게임 프로젝트에서 성능 차이가 매우 중요하다는 것을 알았습니다. 두 가지 유형의 Ref-Counted 포인터가 필요했습니다.
Kylotan

7

마지막 "리조트"가 그가 사용한 정확한 단어인지 기억이 나지 않지만, 그가 말한 실제 의미가 마지막 "선택"이라고 생각합니다. unique_ptr, weak_ptr, shared_ptr 및 심지어 알몸 포인터도 그 자리에 있습니다.

그들이 모두 동의 한 한 가지는 우리가 C ++ 11의 "학습 단계"에 있고 (개발자와 책 저자 등) 패턴과 스타일이 정의되고 있다는 것입니다.

예를 들어 Herb는 C 의 업계 경험과 베스트 프랙티스가 몇 년 동안 유효 C ++ (Meyers) 및 C ++ 코딩 표준 (Sutter & Alexandrescu) 과 같은 일부 C ++ 서적의 새로운 버전을 기대해야한다고 설명 했습니다. ++ 11이 사라집니다.


5

그가 생각하고있는 것은 모든 사람들이 표준 포인터를 작성할 때마다 shared_ptr을 쓰는 것이 일반적이되었고 실제로 디자인하거나 최소한 대신 cop-out으로 사용되고 있다는 것입니다. 객체 생성 및 삭제 계획.

사람들이 잊어 버린 다른 것 (위의 자료에서 언급 한 잠금 / 업데이트 / 잠금 해제 병목 현상 외에도)은 shared_ptr만으로는주기 문제를 해결하지 못한다는 것입니다. shared_ptr을 사용하여 여전히 리소스를 유출 할 수 있습니다.

객체 A는 다른 객체 A에 대한 공유 포인터를 포함합니다. 객체 B는 A a1 및 A a2를 생성하고 a1.otherA = a2를 할당합니다. 및 a2.otherA = a1; 이제 객체 B의 공유 포인터는 a1, a2를 만드는 데 사용되는 범위를 벗어났습니다 (예 : 함수 끝). 이제는 누출이 있습니다. 다른 사람은 a1과 a2를 참조하지 않지만 서로를 참조하므로 참조 횟수는 항상 1이며 유출되었습니다.

이것이 간단한 예제입니다. 실제 코드에서 발생하면 일반적으로 복잡한 방식으로 발생합니다. weak_ptr에는 해결책이 있지만 이제는 많은 사람들이 shared_ptr을 어디에서나 수행하고 누출 문제 또는 weak_ptr조차 알지 못합니다.

마무리하기 위해 : OP가 참조 한 의견은 다음과 같이 요약됩니다.

어떤 언어로 작업하든 (관리, 비 관리 또는 shared_ptr과 같은 참조 횟수를 가진 언어) 개체 생성, 수명 및 파괴를 이해하고 의도적으로 결정해야합니다.

편집 : "알 수없는, 나는 shared_ptr을 사용해야합니다"를 의미하더라도, 당신은 여전히 ​​생각하고 의도적으로 그렇게하고 있습니다.


3

모든 객체가 참조 카운트되고 힙에 할당 되는 언어 인 Objective-C에 대한 경험을 바탕으로 답변 해 드리겠습니다 . 객체를 처리하는 한 가지 방법이 있기 때문에 프로그래머가 훨씬 쉽게 할 수 있습니다. 이를 통해 표준 규칙을 정의하여 준수 할 때 코드 견고성을 보장하고 메모리 누수를 방지 할 수 있습니다. 또한 현명한 컴파일러 최적화가 최근 ARC (자동 참조 카운팅)처럼 나타날 수있었습니다.

내 요점은 shared_ptr이 마지막 수단이 아닌 첫 번째 옵션이어야한다는 것입니다. 수행중인 작업이 확실한 경우에만 기본 및 참조 옵션을 사용하십시오. 생산성이 높아지고 코드가 더욱 강력 해집니다.


1

나는 질문에 대답하려고 노력할 것이다.

std :: shared_ptr없이 어떻게 프로그래밍하고 객체 수명을 안전한 방식으로 관리 할 수 ​​있습니까?

C ++에는 메모리를 수행하는 다양한 방법이 있습니다. 예를 들면 다음과 같습니다.

  1. struct A { MyStruct s1,s2; };클래스 범위에서 shared_ptr 대신 사용하십시오 . 종속성이 작동하는 방식을 이해하고 종속성을 트리로 제한하기에 충분한 종속성을 제어하는 ​​기능이 필요하기 때문에 고급 프로그래머에게만 해당됩니다. 헤더 파일의 클래스 순서는 이것의 중요한 측면입니다. 이 사용법은 이미 기본 제공되는 내장 C ++ 유형에서 일반적이지만, 프로그래머 정의 클래스에서 사용하는 것은 이러한 종속성 및 클래스 순서 문제로 인해 덜 사용되는 것으로 보입니다. 이 솔루션은 또한 sizeof에 문제가 있습니다. 프로그래머는 이것에 대한 문제를 포워드 선언이나 불필요한 #include를 사용하기위한 요구 사항으로보고 있으므로 많은 프로그래머는 포인터의 열등한 솔루션으로, 나중에는 shared_ptr로 넘어갑니다.
  2. MyClass &find_obj(int i);대신 + clone ()을 사용하십시오 shared_ptr<MyClass> create_obj(int i);. 많은 프로그래머들이 새로운 객체를 만들기 위해 팩토리를 만들고 싶어합니다. shared_ptr은 이러한 종류의 사용에 이상적입니다. 문제는 더 간단한 스택 또는 객체 기반 솔루션 대신 힙 / 프리 저장소 할당을 사용하는 복잡한 메모리 관리 솔루션을 이미 가정하고 있다는 것입니다. 좋은 C ++ 클래스 계층 구조는 그 중 하나만이 아니라 모든 메모리 관리 체계를 지원합니다. 반환 된 객체가 로컬 함수 범위 변수를 사용하는 대신 포함하는 객체 내에 저장된 경우 참조 기반 솔루션이 작동 할 수 있습니다. 공장에서 사용자 코드로 소유권을 전달하지 않아야합니다. find_obj ()를 사용한 후 객체를 복사하는 것이 객체를 처리하는 좋은 방법입니다. 다형성 객체에 대한 refrerence 매개 변수 또는 clone ()이있는 일반 복사 생성자와 일반 생성자 (다른 클래스)가 처리 할 수 ​​있습니다.
  3. 포인터 나 shared_ptr 대신에 참조를 사용하십시오. 모든 C ++ 클래스에는 생성자가 있으며 각 참조 데이터 멤버를 초기화해야합니다. 이 사용법은 많은 포인터 및 shared_ptr 사용을 피할 수 있습니다. 메모리가 객체 내부 또는 외부에 있는지 여부를 선택하고 결정에 따라 구조체 솔루션 또는 참조 솔루션을 선택하면됩니다. 이 솔루션의 문제는 일반적으로 생성자 매개 변수를 피하는 것과 관련이 있지만 일반적으로 문제가 있지만 연습과 클래스의 인터페이스를 디자인하는 방법을 오해하고 있습니다.

"공장에서 사용자 코드로 소유권을 전달하지 않아야합니다." 그리고 그것이 불가능할 때 어떻게됩니까? "포인터 또는 shared_ptr 대신 참조 사용." 음 .. 아니야. 포인터를 다시 놓을 수 있습니다. 참조는 할 수 없습니다. 이렇게하면 클래스에 저장된 내용에 대한 시공 시간 제한이 적용됩니다. 그것은 많은 것들에 실용적이지 않습니다. 귀하의 솔루션은보다 유동적 인 인터페이스 및 사용 패턴의 요구에 매우 견고하고 융통성이없는 것으로 보입니다.
Nicol Bolas

@Nicol Bolas : 위의 규칙을 따르면 참조는 제안 된 데이터 저장이 아닌 객체 간의 종속성에 사용됩니다. 종속성은 데이터보다 안정적이므로 고려중인 문제에 빠지지 않습니다.
tp1

다음은 매우 간단한 예입니다. 게임 엔터티가 있는데, 이는 개체입니다. 대화해야 할 대상 엔터티 인 다른 개체를 참조해야합니다. 그러나 목표는 변경 될 수 있습니다. 대상은 다양한 지점에서 죽을 수 있습니다 . 기업은 이러한 상황을 처리 할 수 ​​있어야합니다. 엄격한 노 포인터 접근 방식은 대상이 죽는 것은 물론 대상을 변경하는 것만 큼 단순한 것을 처리 할 수 ​​없습니다.
Nicol Bolas

@nicol bolas : 아, 그것은 다르게 취급된다; 클래스의 인터페이스는 둘 이상의 "엔티티"를 지원합니다. 객체와 엔티티 사이의 1 : 1 매핑 대신 entityarray를 사용합니다. 그런 다음 엔터티를 배열에서 제거하면 엔터티가 매우 쉽게 죽습니다. 전체 게임에 적은 수의 엔티티 배열이 있으며 배열 사이의 종속성은 자주 변경되지 않습니다 :)
tp1

2
아니요, unique_ptr공장에 가장 적합합니다. 당신은 바꿀 수 unique_ptrshared_ptr있지만 다른 방향으로 가고 논리적으로 불가능하다.
벤 Voigt
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.