2009-08-31 2 views
67

업데이트 :이 예에서 shared_ptr는 Boost의 것과 비슷하지만 shared_polymorphic_downcast (또는 해당 문제의 경우 dynamic_pointer_cast 또는 static_pointer_cast)를 지원하지 않습니다!다운 캐스팅 shared_ptr <Base> to shared_ptr <Derived>?

나는 참조 카운트를 잃지 않고 파생 클래스에 공유 포인터를 초기화하기 위해 노력하고있어 :

struct Base { }; 
struct Derived : public Base { }; 
shared_ptr<Base> base(new Base()); 
shared_ptr<Derived> derived; 

// error: invalid conversion from 'Base* const' to 'Derived*' 
derived = base; 

를 지금까지 너무 좋아. C++에서 Base *를 암시 적으로 Derived *로 변환 할 것을 기대하지 않았습니다. 그러나, 나는 코드에 의해 표현 된 기능을 원한다. (즉, 기본 포인터를 내리는 동안 참조 카운트를 유지해야한다.) 내 첫번째 생각은 일어날 수있는 파생에 대한 암시 적 변환 있도록 자료의 캐스트 연산자를 제공하는 것이 었습니다 (의 pedants을 위해 : 나는 아래 캐스트가 유효한지 확인할 것, 걱정하지 마세요) :

struct Base { 
    operator Derived*(); 
} 
// ... 
Base::operator Derived*() { 
    return down_cast<Derived*>(this); 
} 

음, 도움이되지 않았다. 컴파일러는 내 typecast 연산자를 완전히 무시한 것 같습니다. shared_ptr 할당을 어떻게 할 수 있습니까? 추가 포인트 : Base* const은 어떤 종류입니까? const Base* 나는 이해하지만, Base* const? 이 경우 const은 무엇을 말합니까?

+0

shared_ptr 대신 shared_ptr 이 필요한 이유는 무엇입니까? – Bill

+2

객체를 복제하지 않고 기본에없는 파생 된 기능에 액세스하려고하므로 (두 개의 공유 포인터로 참조되는 단일 객체가 필요함). 그건 그렇고, 캐스팅 오퍼레이터는 왜 작동하지 않습니까? –

답변

45

나는 당신이 boost::shared_ptr을 사용하고 있다고 가정합니다. 나는 dynamic_pointer_cast 또는 shared_polymorphic_downcast이 필요하다고 생각합니다.

그러나 이러한 유형에는 다형성 유형이 필요합니다.

어떤 종류의 종류가 Base* const입니까? const Base* 나는 이해하지만, Base* const? 이 경우 const은 무엇을 말합니까?

  • const Base *Base 상수로 변경할 포인터이다.
  • Base const *은 상수 Base에 대한 변경 가능한 포인터입니다.
  • Base * const은 변경 가능 포인터 Base에 대한 상수 포인터입니다.
  • Base const * const은 상수 Base에 대한 상수 포인터입니다.

여기에 최소한의 예입니다 :

struct Base { virtual ~Base() { } }; // dynamic casts require polymorphic types 
struct Derived : public Base { }; 

boost::shared_ptr<Base> base(new Base()); 
boost::shared_ptr<Derived> derived; 
derived = boost::static_pointer_cast<Derived>(base); 
derived = boost::dynamic_pointer_cast<Derived>(base); 
derived = boost::shared_polymorphic_downcast<Derived>(base); 

내가 당신의 예는 기본 유형의 인스턴스를 생성하고 캐스트 것을 의도적이라면 모르겠지만, 멋지게 차이를 설명하는 역할을한다.

static_pointer_cast은 "그냥 해". 이로 인해 정의되지 않은 동작이 발생하고 (은 메모리를 가리키며 Base에 의해 초기화되고 초기화 됨) 충돌이 발생할 수 있습니다. base의 참조 횟수가 증가합니다.

dynamic_pointer_cast은 널 포인터가됩니다. base의 참조 횟수는 변경되지 않습니다.

shared_polymorphic_downcast은 정적 캐스트와 동일한 결과를 가지지 만 성공하지 못하고 정의되지 않은 동작으로 이어지는 대신 어설 션을 트리거합니다. base의 참조 횟수가 증가합니다.

이 때로는 static_cast 또는 dynamic_cast을 사용할지 여부를 결정하기 어려운 조금, 당신은 당신이 두 세계의 조금을 가질 수 소원 :

(dead link)를 참조하십시오. dynamic_cast에는 런타임 오버 헤드가 있지만 잘 알려져 있지만 static_cast에는 오버 헤드가 전혀 없지만 자동으로 실패 할 수 있습니다. 디버그 빌드에서 shared_dynamic_cast을 사용하고 릴리스 빌드에서 shared_static_cast을 사용할 수 있다면 얼마나 좋을까요? 음, 그런 것들은 이미 이용 가능하며 shared_polymorphic_downcast이라고합니다.

+0

불행히도, 솔루션은 우리가 사용하고있는 특정 shared_ptr 구현에서 의도적으로 제외 된 부스트 기능에 의존합니다 (이유를 묻지 마십시오). const 설명에 관해서는 훨씬 더 이해가됩니다. –

+3

다른'shared_ptr' 생성자 ('static_cast_tag'와'dynamic_cast_tag'를 취하는 것)를 구현하기에는 부족합니다. 'shared_ptr' 밖에서하는 일은 refcount를 관리 할 수 ​​없습니다. - "완벽한"객체 지향 디자인에서는 기본 유형을 항상 사용할 수 있으며 파생 된 유형이 무엇인지 알 필요도 신경도 쓰지 않아도됩니다. 왜냐하면 모든 기능이 기본 클래스 인터페이스를 통해 노출되기 때문입니다. 아마도 당신은 왜 당신이 처음부터 다운 캐스팅해야하는지 다시 생각할 필요가있을 것입니다. –

+1

@Tim Sylvester :하지만 C++은 "완벽한"OO 언어가 아닙니다! :-) 다운 캐스트는 완벽하지 않은 OO 언어로되어 있습니다. –

55

dynamic_pointer_cast을 사용할 수 있습니다. std::shared_ptr에 의해 지원됩니다.

std::shared_ptr<Base> base (new Derived()); 
std::shared_ptr<Derived> derived = 
       std::dynamic_pointer_cast<Derived> (base); 

또한 기본 클래스에서 캐스트 연산자를 사용하지 않는 것이 좋습니다. 이와 같이 암시 적으로 캐스팅하면 버그와 오류의 원인이 될 수 있습니다.

- 업데이트 : 유형이 다형성이 아닌 경우 std::static_pointer_cast을 사용할 수 있습니다. 누군가가 부스트 :: shared_ptr을 여기 들어가면

+3

첫 줄에서'std :: shared_ptr'을 사용하지 않는다는 것을 이해하지 못했습니다. 하지만 첫 번째 대답의 코멘트에서 나는 그가 부스트를 사용하지 않는다고 추측했기 때문에'std :: shared_ptr'을 사용하고 있을지도 모른다. –

+0

OK. 죄송합니다. 그는 자신이 맞춤 구현을 사용하고 있음을 명확히해야합니다. –

3

은 ...

이것은 당신이 파생 부스트 shared_ptr을로 다운 캐스트 할 수있는 방법입니다. 가정에서 파생 된 가정 자료.

boost::shared_ptr<Base> bS; 
bS.reset(new Derived()); 

boost::shared_ptr<Derived> dS = boost::dynamic_pointer_cast<Derived,Base>(bS); 
std::cout << "DerivedSPtr is: " << std::boolalpha << (dS.get() != 0) << std::endl; 

'Base'class/struct에 적어도 하나의 가상 함수가 있는지 확인하십시오. 가상 소멸자도 작동합니다.