최후의 수단으로 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_ptr와 weak_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_ptr에 shared_ptr있지만 다른 방향으로 가고 논리적으로 불가능하다.
— 벤 Voigt
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.