2016-11-15 2 views
4

Scott Meyers가 Effective Modern C++를 읽고 있는데 그는 pimpl 관용구의 사용을 논의하고 구현 클래스를 unique_ptr으로 가리키고 있지만 특별한 멤버 함수의 문제가 있습니다 (예 : 소멸자로서) 형식을 완성해야합니다. 그 이유는 unique_ptr의 기본 삭제자가 delete p이 사용되기 전에 삭제할 유형이 완료되었는지 정적으로 선언하기 때문입니다. 따라서 클래스의 모든 특수 멤버 함수는 구현 클래스가 정의 된 후에 구현 파일 (컴파일러 생성 대신 생성)에 정의되어야합니다.불완전한 타입을 사용하는 shared_ptr을 사용하는 Pimpl 관용어

스마트 포인터가 shared_ptr 인 경우 구현 파일에 특별한 멤버 함수를 정의 할 필요가 없다고 언급하고 있는데, 이는 사용자 정의 Deleter를 지원하는 방식에 기인합니다. 인용하자면 :

의 차이를 표준 : : unique_ptr 및 표준 : : shared_ptr의 사이에 동작에 pImpl 포인터에 대한 것은이 스마트 포인터가 정의 deleters을 지원하는 다른 방법에서 유래한다. std :: unique_ptr의 경우 삭제 기 유형이 스마트 포인터 유형의 일부이므로 컴파일러가 더 작은 런타임 데이터 구조 및 더 빠른 런타임 코드를 생성 할 수 있습니다. 이러한 효율성 향상의 결과로 컴파일러에서 생성 한 특수 기능 (예 : 소멸자 또는 이동 작업)을 사용할 때 가리키는 유형이 완료되어야합니다. std :: shared_ptr의 경우 deleter 유형은 스마트 포인터 유형의 일부가 아닙니다. 이를 위해서는 더 큰 런타임 데이터 구조와 다소 느린 코드가 필요하지만 컴파일러가 생성 한 특수 기능을 사용할 때는 가리키는 형식을 으로 완료 할 필요는 없습니다.

그럼에도 불구하고 아직 완료되지 않은 채로 shared_ptr이 작동하는 이유는 여전히 알 수 없습니다. shared_ptr을 사용할 때 컴파일러 오류가없는 유일한 이유는 unique_ptr과 같은 정적 어썰트가 없기 때문이며이 어설 션 부족으로 인해 정의되지 않은 런타임 동작이 발생할 수 있기 때문입니다.

나는 그것이 같은 작동 인상 수집 (C++ 입문서를 읽고)을 shared_ptr의 소멸자의 구현을 알고 있지만하지 않습니다 del가 포인터 또는 함수 객체이다

del ? del(p) : delete p; 

을 커스텀 Deleter. Cppreference 또한 어떤 사용자 정의 Deleter가가 배열 유형이 아닌 delete ptrT 경우 delete p

3) 삭제 표현을 사용하여 사용하지 않습니다와는 shared_ptr 소멸자을 취소합니다; .... Y는 완전한 유형이어야합니다. 삭제 표현식은 잘 형성되고, 잘 정의 된 동작을 가져야하며 예외를 throw하지 않아야합니다.

삭제 된 유형이 완료되어야한다는 사실에 중점을 둡니다.pimpl 관용구의 최소한 예 :

main.cppWidget a 컴파일되면 shared_ptr의 템플릿 (main.cpp 이내) 형 Widget위한 instantited되고 아마도 shared_ptr 대한 결과 컴파일 소멸자 라인 delete pImpl의 실행을 포함
//widget.h 

#ifndef WIDGET 
#define WIDGET 

#include <memory> 

class Widget{ 
public: 
    Widget(); 
private: 
    struct Impl; 
    std::shared_ptr<Impl> pImpl; 

}; 

#endif // WIDGET 

//widget.cpp 

#include <string> 
#include "Widget.h" 

struct Widget::Impl{ 
    std::string name; 
}; 

Widget::Widget(): pImpl(new Impl) {} 

//main.cpp 

#include <iostream> 
#include "Widget.h" 

int main(){ 
    Widget a; 
} 

, 나는 사용자 정의 삭제자를 제공하지 않았기 때문에. 그러나이 시점에서 Impl은 아직 정의되지 않았지만 delete pImpl 행이 실행됩니다. 이것은 분명히 정의되지 않은 동작입니까?

shared_ptr으로 pimpl 관용구를 사용할 때 어떻게 정의되지 않은 동작을 피하기 위해 구현 파일에 특수 멤버 함수를 정의 할 필요가 없습니까? 공유 포인터

답변

6

Deleter가 여기에 생성됩니다

Widget::Widget(): pImpl(new Impl) {} 

그 시점까지, 모든 공유 포인터가 std::funciton<void(Impl*)>의 것과 동일했다.

T*으로 shared_ptr을 구성하면 삭제 기가 쓰여지고 std::function에 해당하는 문자가 저장됩니다. 이 시점에서 유형이 완료되어야합니다.

따라서 Impl 이후에 정의해야하는 기능은 T*에서 어떤 종류의 pImpl을 만드는 기능뿐입니다.

+0

'shared_ptr' 소멸자가 직접'delete' 함수를 호출했다는 가정하에, 커스텀이 제공되지 않았다면 함수 객체를 통해 함수를 호출합니다. 감사. – SergeantPenguin

+0

나는 이것을 내 머리 속에서 정리해달라고 요청해야만한다. Deleter가 "여기"에서 만들어 졌다고 말할 때, 나는 이것이 폐쇄로 끝났다고 가정한다. 'shared_ptr'에 대한 정확한 디폴트 삭제 자에 대한 세부 사항을 찾을 수 없었습니다 ('unique_ptr'가'std :: default_delete'를 사용함을 보았지만). – SergeantPenguin

+0

@searg 효과가 지정되며 구현은 지정되지 않습니다. – Yakk

관련 문제