shared_ptr 매직 :)


89

Mr. Lidström과 저는 논쟁을했습니다. :)

Lidström의 주장은 구조 shared_ptr<Base> p(new Derived);가 Base에 가상 소멸자를 필요로하지 않는다는 것입니다 .

Armen Tsirunyan : "정말? shared_ptr이 올바르게 정리 될까요?이 경우 어떻게 그 효과를 구현할 수 있는지 보여 주시겠습니까?"

Daniel Lidström : " shared_ptr 는 자체 소멸자를 사용하여 Concrete 인스턴스를 삭제합니다. 이것은 C ++ 커뮤니티 내에서 RAII로 알려져 있습니다. 제 조언은 RAII에 대해 가능한 모든 것을 배우는 것입니다. 사용하면 C ++ 코딩이 훨씬 쉬워 질 것입니다. 모든 상황에서 RAII. "

Armen Tsirunyan : "RAII에 대해 알고 있습니다. 또한 pn이 0에 도달하면 shared_ptr 소멸자가 저장된 px를 삭제할 수 있다는 것도 알고 있습니다. 그러나 px 에에 대한 정적 유형 포인터 Base와에 대한 동적 유형 포인터 가있는 경우 가상 소멸자가 Derived없는 경우 Base정의되지 않은 동작이 발생합니다. 내가 틀렸다면 수정하십시오. "

Daniel Lidström : " shared_ptr 은 정적 유형이 Concrete라는 것을 알고 있습니다. 생성자에 전달했기 때문에 이것을 알고 있습니다! 약간 마술처럼 보이지만 설계 상 매우 훌륭하다고 확신 할 수 있습니다."

그래서 우리를 판단하십시오. 가상 소멸자를 갖기 위해 다형성 클래스를 요구하지 않고 shared_ptr 을 구현하는 것이 어떻게 가능 합니까? 미리 감사드립니다


3
원래 스레드에 연결할 수 있습니다.
Darin Dimitrov

8
또 다른 흥미로운 점은 그것이 있는지 여부에 관계없이 소멸자에 의해 객체를 shared_ptr<void> p(new Derived)파괴 한다는 것 입니다. Derivedvirtual
dalle

7
질문하는 멋진 방법 :)
rubenvb

5
shared_ptr이 이것을 허용하더라도 가상 dtor없이 클래스를베이스로 디자인하는 것은 정말 나쁜 생각 입니다. RAII에 대한 Daniel의 발언은 오해의 소지가 있습니다. 이는 이와 관련이 없습니다. 그러나 인용 된 대화는 단순한 오해 (그리고 shared_ptr 작동 방식에 대한 잘못된 가정)처럼 들립니다.

6
RAII가 아니라 소멸자를 입력 삭제합니다. 당신이 있기 때문에 조심해야 shared_ptr<T>( (T*)new U() )어디 struct U:T옳은 일을하지 않을 것이다 (이 같은 소요 함수로, 간접적으로 쉽게 수행 할 수 있습니다 T*및 전달됩니다 U*)
Yakk - 아담 Nevraumont

답변:


74

예, 그런 방식으로 shared_ptr을 구현할 수 있습니다. Boost는 그렇고 C ++ 11 표준도이 동작을 요구합니다. 추가 된 유연성으로 shared_ptr은 단순한 참조 카운터 이상을 관리합니다. 소위 삭제자는 일반적으로 참조 카운터도 포함하는 동일한 메모리 블록에 배치됩니다. 그러나 재미있는 부분은이 삭제 자의 유형이 shared_ptr 유형의 일부가 아니라는 것입니다. 이것을 "유형 삭제"라고하며 기본적으로 실제 펑터의 유형을 숨기기 위해 "다형성 함수"boost :: function 또는 std :: function을 구현하는 데 사용되는 것과 동일한 기술입니다. 예제가 작동하도록하려면 템플릿 생성자가 필요합니다.

template<class T>
class shared_ptr
{
public:
   ...
   template<class Y>
   explicit shared_ptr(Y* p);
   ...
};

따라서 이것을 Base 및 Derived 클래스와 함께 사용하면 ...

class Base {};
class Derived : public Base {};

int main() {
   shared_ptr<Base> sp (new Derived);
}

... Y = Derived 인 템플릿 생성자는 shared_ptr 객체를 생성하는 데 사용됩니다. 따라서 생성자는 적절한 삭제 자 개체와 참조 카운터를 만들고이 제어 블록에 대한 포인터를 데이터 멤버로 저장할 수 있습니다. 참조 카운터가 0에 도달하면 이전에 생성되고 파생 인식 삭제 기가 개체를 처리하는 데 사용됩니다.

C ++ 11 표준에는이 생성자 (20.7.2.2.1)에 대해 다음과 같은 내용이 있습니다.

필요 : p 로 변환 할 수 있어야합니다 T*. Y완전한 유형이어야합니다. 표현 delete p은 잘 형성되어야하고 잘 정의 된 행동을 가져야하며 예외를 던지지 않아야합니다.

효과 : 포인터 소유shared_ptr개체 를 생성 합니다 .p

소멸자 (20.7.2.2.2)의 경우 :

효과 :*this비어 있거나 다른 shared_ptr인스턴스 ( use_count() > 1) 와 소유권을 공유 하면 부작용이 없습니다. 그렇지 않으면 *this객체를 소유 한 경우p 하고 Deleter가를 d, d(p)이라고합니다. 그렇지 않은 경우는 *this포인터를 소유 p하고, delete p라고합니다.

(굵은 글꼴을 사용한 강조는 내 것입니다).


the upcoming standard also requires this behaviour: (a) 어떤 표준과 (b) 표준에 대한 참조를 제공 할 수 있습니까?
kevinarpe 2016 년

나는 충분한 포인트가 없기 때문에 @sellibitze의 답변에 코멘트를 추가하고 싶습니다 add a comment. IMO Boost does this이상 the Standard requires입니다. 나는 표준이 내가 이해하고있는 것을 요구하지 않는다고 생각합니다. @sellibitze의 예에 대해 이야기 shared_ptr<Base> sp (new Derived);, 필요constructor단지를 요청 delete Derived되고 잘 정의 된 잘 형성했다. 의 사양에 대해서는. destructorp있지만 . p의 사양에서를 참조한다고 생각하지 않습니다 constructor.
Lujun Weng

28

shared_ptr이 생성되면 삭제 자를 저장합니다. 자체 내부 개체. 이 객체는 shared_ptr이 지적 자원을 해제하려고 할 때 호출됩니다. 생성 시점에서 리소스를 삭제하는 방법을 알고 있으므로 불완전한 유형으로 shared_ptr을 사용할 수 있습니다. shared_ptr을 만든 사람은 거기에 올바른 삭제자를 저장했습니다.

예를 들어 사용자 지정 삭제자를 만들 수 있습니다.

void DeleteDerived(Derived* d) { delete d; } // EDIT: no conversion needed.

shared_ptr<Base> p(new Derived, DeleteDerived);

p는 뾰족한 개체를 파괴하기 위해 DeleteDerived를 호출합니다. 구현은이를 자동으로 수행합니다.


4
불완전한 유형에 대한 설명은 +1 shared_ptr. 속성으로 사용할 때 매우 편리 합니다.
Matthieu M.

16

간단히,

shared_ptr Base의 소멸자가 아닌 지정된 객체의 소멸자를 항상 사용하는 생성자에 의해 생성되는 특수 삭제 기능을 사용합니다. 이것은 템플릿 메타 프로그래밍에서 약간의 작업이지만 작동합니다.

그런 것

template<typename SomeType>
shared_ptr(SomeType *p)
{
   this->destroyer = destroyer_function<SomeType>(p);
   ...
}

1
흠 ... 흥미 롭습니다. 저는 이것을 믿기 시작했습니다. :)
Armen Tsirunyan

1
@Armen Tsirunyan 토론을 시작하기 전에 shared_ptr의 디자인 설명을 살펴 보았어야합니다. 이 '는 Deleter가의 캡처'...있는 shared_ptr의 필수 기능 중 하나입니다
폴 미칼

6
@ paul_71 : 동의합니다. 다른 한편으로 나는이 토론이 나뿐만 아니라 shared_ptr에 대해이 사실을 모르는 다른 사람들에게도 유용하다고 생각합니다. 그래서 어쨌든이 스레드를 시작하는 것은 큰 죄가 아니었다 고 생각합니다. :)
Armen Tsirunyan

3
@Armen 물론 아닙니다. 오히려 숙련 된 C ++ 개발자도 자주 감독하는 shared_ptr <T>의 매우 매우 중요한 기능을 지적하는 데 잘했습니다.
Paul Michalik
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.