shared_ptr <Base>를 shared_ptr <Derived>로 다운 캐스팅 하시겠습니까?


102

업데이트 : 이 예제의 shared_ptr은 Boost의 것과 비슷하지만 shared_polymorphic_downcast (또는 해당 문제에 대해 dynamic_pointer_cast 또는 static_pointer_cast)를 지원하지 않습니다!

참조 횟수를 잃지 않고 파생 클래스에 대한 공유 포인터를 초기화하려고합니다.

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 *로 암시 적으로 변환 할 것이라고는 예상하지 못했습니다. 그러나 코드로 표현 된 기능 (즉, 기본 포인터를 다운 캐스팅하는 동안 참조 횟수 유지)을 원합니다. 내 첫 번째 생각은 Derived 로의 암시 적 변환이 발생할 수 있도록 Base에 캐스트 연산자를 제공하는 것이 었습니다 (행동 자의 경우 : 다운 캐스트가 유효한지 확인합니다. 걱정하지 마십시오).

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

음, 도움이되지 않았습니다. 컴파일러가 내 typecast 연산자를 완전히 무시한 것 같습니다. shared_ptr 할당을 작동시키는 방법에 대한 아이디어가 있습니까? 추가 점수 : 어떤 유형 Base* const입니까? const Base*이해 합니다만 Base* const? 무엇 않습니다 const이 경우 다음을 참조?


shared_ptr <Base> 대신 shared_ptr <Derived>가 필요한 이유는 무엇입니까?
Bill

3
객체를 복제하지 않고 Base에없는 Derived의 기능에 액세스하고 싶기 때문입니다 (두 개의 공유 포인터가 참조하는 단일 객체를 원합니다). 그런데 캐스트 연산자가 작동하지 않는 이유는 무엇입니까?
Lajos Nagy

답변:


109

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

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

문서 : https://en.cppreference.com/w/cpp/memory/shared_ptr/pointer_cast

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

-업데이트 : 유형이 다형성이 아닌 경우 사용할std::static_pointer_cast 수 있습니다.


4
나는 그가 사용하지 않는 첫 번째 줄에서 이해하지 못했습니다 std::shared_ptr. 그러나 첫 번째 답변의 의견에서 그가 부스트를 사용하지 않는다고 추론했기 때문에 std::shared_ptr.
Massood Khaari

확인. 죄송합니다. 그는 사용자 정의 구현을 사용하고 있음을 더 명확히해야합니다.
Massood Khaari

47

나는 당신이 사용하고 있다고 가정합니다 boost::shared_ptr... 나는 당신이 원 dynamic_pointer_cast하거나 shared_polymorphic_downcast.

그러나 이들은 다형성 유형이 필요합니다.

어떤 유형 Base* const입니까? const Base*이해 합니다만 Base* const? 무엇 않습니다 const이 경우 다음을 참조?

  • const Base *상수에 대한 가변 포인터 Base입니다.
  • Base const *상수에 대한 가변 포인터 Base입니다.
  • Base * constmutable에 대한 상수 포인터 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의지 "그냥 해." 이로 인해 정의되지 않은 동작 (에 Derived*의해 할당되고 초기화 된 메모리를 가리킴 Base)이 발생하고 충돌이 발생하거나 더 나빠질 수 있습니다. 참조 횟수 base가 증가합니다.

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

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

참조 (데드 링크) :

static_cast또는 을 사용할지 여부를 결정하는 것이 약간 어려울 때가 있으며 dynamic_cast, 두 세계를 조금이라도 가질 수 있기를 바랍니다. dynamic_cast에는 런타임 오버 헤드가 있지만 더 안전하지만 static_cast에는 오버 헤드가 전혀 없지만 자동으로 실패 할 수 있다는 것은 잘 알려져 있습니다. shared_dynamic_cast디버그 빌드와 shared_static_cast릴리스 빌드에서 사용할 수 있다면 얼마나 좋을까요? 글쎄, 그런 것은 이미 사용 가능하며 shared_polymorphic_downcast.


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

3
다른 shared_ptr생성자 ( static_cast_tagdynamic_cast_tag) 를 구현 하지 않으면 할 수있는 일이별로 없습니다. 외부에서 수행하는 모든 작업 shared_ptr은 refcount를 관리 할 수 ​​없습니다. - "완벽한"OO 디자인에서는 항상 기본 유형을 사용할 수 있으며 모든 기능이 기본 클래스 인터페이스를 통해 노출되기 때문에 파생 유형이 무엇인지 알거나 신경 쓸 필요가 없습니다. 아마도 당신은 애초에 왜 다운 캐스트를해야하는지 다시 생각할 필요가있을 것입니다.
Tim Sylvester

1
@Tim Sylvester :하지만 C ++는 "완벽한"OO 언어가 아닙니다! :-) 다운 캐스트는 완벽하지 않은 OO 언어로 자리를 잡았습니다
Steve Folly

4

누군가 boost :: shared_ptr로 여기에 오면 ...

이것이 파생 된 Boost shared_ptr로 다운 캐스트하는 방법입니다. Derived가 Base에서 상속한다고 가정합니다.

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'클래스 / 구조체에 가상 함수가 하나 이상 있는지 확인하십시오. 가상 소멸자도 작동합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.