18.10 Dynamic casting
lesson 8.5 -- Explicit type conversion (casting) and static_cast,에서
우리는 casting에 대한 개념에 대해서 짚고 넘어갔다.
이번 lesson에서는 dynamic cast라는 또 다른 타입으 casting에 대해서 살펴본다
The need for dynamic_cast
polymorphism을 다룰 때 우리는 종종 base class의 pointer를 가지고 있을 때 derived class의 정보에 접근하고 싶을 때가 있다
다음 예시를 살펴보자
#include <iostream>
#include <string>
class Base
{
protected:
int m_value{};
public:
Base(int value)
: m_value{value}
{
}
virtual ~Base() = default;
};
class Derived : public Base
{
protected:
std::string m_name{};
public:
Derived(int value, const std::string& name)
: Base{value}, m_name{name}
{
}
const std::string& getName() const { return m_name; }
};
Base* getObject(bool returnDerived)
{
if (returnDerived)
return new Derived{1, "Apple"};
else
return new Base{2};
}
int main()
{
Base* b{ getObject(true) };
// how do we print the Derived object's name here, having only a Base pointer?
delete b;
return 0;
}
위 프로그램에서 getObject 함수는 항상 Base pointer를 반환한다. 그러나 pointer는 Base 혹은 Derived object 둘 다 pointing할 수 있다. 이러한 상황에서 pointer가 Derived object를 pointing하는 경우 우리는 어떻게 Derived::getName()을 호출할 수 있을까?
한가지 방법은 virtual function을 사용하는 것이다
하지만 Base object를 pointing하는 Base pointer의 경우에는 어떻게 해야하나?
이는 의미가 없다. 게다가 우리는 Base class를 오염시킬 수도 있다
우리는 c++에서 implicitly하게 Derived pointer를 Base pointer로 convert해주는 것을 알고 있다. 이러한 과정을 upcasting이라고 한다
그러나 Base 포인터를 Derived 포인터로 다시 변환하는 방법이 있다면 어떨까? 그렇다면 우리는 Derived::getName()을 직접적으로 호출할 수 있을 것이다. virtual에 상관없이
dynamic_cast
c++은 casting operator로 dynamic_cast 라는 것을 지원한다
이는 이러한 목적으로 사용가능하다.
dynamic casting에는 몇 가지 다른 기능이 있지만 dynamic casting의 가장 일반적인 용도는 Base 클래스 포인터를 Derived 클래스 포인터로 변환하는 것입니다.
이를 upcasting의 반댓말인 downcasting 이라고 한다
dynamic_cast는 static_cast 처럼 사용하면 된다
여기 예시가 있다
int main()
{
Base* b{ getObject(true) };
Derived* d{ dynamic_cast<Derived*>(b) }; // use dynamic cast to convert Base pointer into Derived pointer
std::cout << "The name of the Derived is: " << d->getName() << '\n';
delete b;
return 0;
}
getObject로 수령한 Base pointer는 Derived object를 pointing하고 있다
그리고 이를 dynamic_cast로 다시 Derived pointer로 downcasting 하고 있다
dynamic_cast failure
위의 예시는 잘 작동한다. 왜냐하면 b object가 Derived Object를 pointing하는 pointer이기 때문이다
그러나 그렇지 않은 경우에는 어떻게 될 것인가?
이는 getObject의 argument를 false로 바꿈으로 간단하게 테스트 할수 있다
만약 우리가 이러한 dynamic_cast를 시도한다면 아마 실패할 것이다
만약 dynamic_cast가 실패하면 결과는 null pointer일 것이다
따라서 우리는 다음과 같이 코드를 작성해야 한다
int main()
{
Base* b{ getObject(true) };
Derived* d{ dynamic_cast<Derived*>(b) }; // use dynamic cast to convert Base pointer into Derived pointer
if (d) // make sure d is non-null
std::cout << "The name of the Derived is: " << d->getName() << '\n';
delete b;
return 0;
}
if (d)를 통해 null pointer인지 체크를 하고 아닌 경우에 d->getName()의 접근을 허용한다
Rule
Always ensure your dynamic casts actually succeeded by checking for a null pointer result.
dynamic_cast는 consistency checking을 runtime에서(conversion이 가능한지 보장하기 위해서) 하므로 성능의 저하를 야기시킨다
또한 여기 dynamic_cast를 통한 downcasting이 실패할 수 잇는 경우가 있다
- With protected or private inheritance.
- For classes that do not declare or inherit any virtual functions (and thus don’t have a virtual table).
- In certain cases involving virtual base classes (see this page for an example of some of these cases, and how to resolve them).
Downcasting with static_cast
Downcasting은 static_cast로도 할 수 있다. 가장 큰 차이점은 static_cast의 경우 runtime type check를 하지 않는다. 이는 static_cast를 사용하는 것이 더 빠르지만 위험해진다는 것을 뜻한다.
Base 포인터가 Derived object를 pointing하는 것이 아닌 경우에도 Base pointer에서 Derived pointer로 변환은 성공한다. 이는 우리가 의도치 않은 동작이다
따라서 우리가 static_cast를 통해 Downcasting을 하고 싶다면 먼저 Derived class object를 pointing하고 있는지 확인해야 한다
다음의 예시에서는 virtual function으로 이를 확인한다
#include <iostream>
#include <string>
// Class identifier
enum class ClassID
{
base,
derived
// Others can be added here later
};
class Base
{
protected:
int m_value{};
public:
Base(int value)
: m_value{value}
{
}
virtual ~Base() = default;
virtual ClassID getClassID() const { return ClassID::base; }
};
class Derived : public Base
{
protected:
std::string m_name{};
public:
Derived(int value, const std::string& name)
: Base{value}, m_name{name}
{
}
const std::string& getName() const { return m_name; }
virtual ClassID getClassID() const { return ClassID::derived; }
};
Base* getObject(bool bReturnDerived)
{
if (bReturnDerived)
return new Derived{1, "Apple"};
else
return new Base{2};
}
int main()
{
Base* b{ getObject(true) };
if (b->getClassID() == ClassID::derived)
{
// We already proved b is pointing to a Derived object, so this should always succeed
Derived* d{ static_cast<Derived*>(b) };
std::cout << "The name of the Derived is: " << d->getName() << '\n';
}
delete b;
return 0;
}
번거롭게 이러한 고려를 하고싶지 않다면 그냥 dynamic_cast를 사용하자
dynamic_cast and references
위의 모든 예들은 pointer의 경우에 dynamic casting에 (더 일반적임) 대해서 보여주고 있지만
dynamic_cast는 reference에도 사용할 수 있다. 이것은 dynamic_cast가 포인터와 함께 작동하는 방식과 유사하게 작동한다
#include <iostream>
#include <string>
class Base
{
protected:
int m_value;
public:
Base(int value)
: m_value{value}
{
}
virtual ~Base() = default;
};
class Derived : public Base
{
protected:
std::string m_name;
public:
Derived(int value, const std::string& name)
: Base{value}, m_name{name}
{
}
const std::string& getName() const { return m_name; }
};
int main()
{
Derived apple{1, "Apple"}; // create an apple
Base& b{ apple }; // set base reference to object
Derived& d{ dynamic_cast<Derived&>(b) }; // dynamic cast using a reference instead of a pointer
std::cout << "The name of the Derived is: " << d.getName() << '\n'; // we can access Derived::getName through d
return 0;
}
c++은 null reference가 없으므로 casting에 실패하면 std::bad_cast가 반환된다
dynamic_cast vs static_cast
신입 프로그래머는 가끔식 static_cast와 dynamic_cast의 사용에 혼란스러워 한다
답은 매우 간단하다. downcasting이 아니면 static_cast를 사용하고
만약 downcasting인 경우 보통 dynamic_cast가 적절한 선택이다
하지만 virtual로 이를 우회하는 방법도 고려해야 한다
Downcasting vs virtual functions
몇몇 개발자들은 dynamic_cast가 나쁘고 나쁜 클래스 디자인을 나타낸다고 믿는다
대신에 virtual function을 사용하라고 말한다
일반적으로 downcasting 보다는 virtual function을 사용하는 것이 선호된다
하지만 downcasting이 더 나은 선택인 경우도 있다
- When you can not modify the base class to add a virtual function (e.g. because the base class is part of the standard library)
- When you need access to something that is derived-class specific (e.g. an access function that only exists in the derived class)
- When adding a virtual function to your base class doesn’t make sense (e.g. there is no appropriate value for the base class to return). Using a pure virtual function may be an option here if you don’t need to instantiate the base class.
Author And Source
이 문제에 관하여(18.10 Dynamic casting), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@ikmy0ung/18.10-Dynamic-casting저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)