이런 종류의 것을 논의하는 거의 모든 C ++ 리소스는 RTTI (런타임 유형 식별)를 사용하는 것보다 다형성 접근 방식을 선호해야한다고 알려줍니다. 일반적으로 저는 이런 종류의 조언을 진지하게 받아들이고 그 근거를 이해하려고 노력할 것입니다. 결국 C ++는 강력한 짐승이며 전체적으로 이해하기 어렵습니다. 그러나이 특정 질문에 대해서는 공백을 그리고 있으며 인터넷이 어떤 종류의 조언을 제공 할 수 있는지보고 싶습니다. 먼저 RTTI가 "유해한 것으로 간주되는"이유에 대해 인용 된 일반적인 이유를 나열하여 지금까지 배운 내용을 요약하겠습니다.
일부 컴파일러는 사용하지 않음 / RTTI가 항상 활성화되지는 않습니다.
나는 정말로이 논쟁을 사지 않는다. C ++ 14 기능을 지원하지 않는 컴파일러가 있기 때문에 C ++ 14 기능을 사용하지 말아야한다고 말하는 것과 같습니다. 그러나 아무도 C ++ 14 기능을 사용하는 것을 낙담하지 않을 것입니다. 대부분의 프로젝트는 사용중인 컴파일러와 구성 방법에 영향을줍니다. gcc 맨 페이지를 인용해도 :
-fno-rtti
C ++ 런타임 유형 식별 기능 (dynamic_cast 및 typeid)에서 사용할 가상 함수가있는 모든 클래스에 대한 정보 생성을 비활성화합니다. 언어의 해당 부분을 사용하지 않는 경우이 플래그를 사용하여 공간을 절약 할 수 있습니다. 예외 처리는 동일한 정보를 사용하지만 G ++는 필요에 따라이를 생성합니다. dynamic_cast 연산자는 런타임 유형 정보가 필요하지 않은 캐스트 (예 : "void *"또는 명확한 기본 클래스로 캐스트)에 계속 사용할 수 있습니다.
이것이 의미하는 바는 RTTI를 사용하지 않는 경우 비활성화 할 수 있다는 것입니다. 이는 Boost를 사용하지 않는 경우 링크 할 필요가 없다는 것과 같습니다. 누군가가 .NET으로 컴파일하는 경우를 계획 할 필요가 없습니다 -fno-rtti
. 또한이 경우 컴파일러는 크고 명확하게 실패합니다.
추가 메모리 비용 / 느릴 수 있음
RTTI를 사용하고 싶을 때마다 일종의 유형 정보 나 클래스 특성에 액세스해야한다는 의미입니다. RTTI를 사용하지 않는 솔루션을 구현하는 경우 이는 일반적으로이 정보를 저장하기 위해 클래스에 일부 필드를 추가해야 함을 의미하므로 메모리 인수는 일종의 무효입니다 (이에 대한 예제를 더 아래에서 설명하겠습니다).
dynamic_cast는 실제로 느릴 수 있습니다. 그러나 일반적으로 속도가 중요한 상황에서 사용하지 않는 방법이 있습니다. 그리고 나는 대안을 잘 보지 못합니다. 이 SO 답변 은 기본 클래스에 정의 된 열거 형을 사용하여 유형을 저장하는 것을 제안합니다. 모든 파생 클래스를 미리 알고있는 경우에만 작동합니다. 그것은 상당히 큰 "if"입니다!
그 대답에서 RTTI의 비용도 명확하지 않은 것 같습니다. 다른 사람들은 다른 것을 측정합니다.
우아한 다형성 디자인으로 RTTI 불필요
이것이 제가 진지하게 받아들이는 조언입니다. 이 경우 RTTI 사용 사례를 다루는 좋은 비 RTTI 솔루션을 찾을 수 없습니다. 예를 들어 보겠습니다.
어떤 종류의 개체의 그래프를 처리하기 위해 라이브러리를 작성하고 있다고 가정 해 보겠습니다. 사용자가 내 라이브러리를 사용할 때 자신의 유형을 생성하도록 허용하고 싶습니다 (따라서 enum 메소드를 사용할 수 없음). 내 노드에 대한 기본 클래스가 있습니다.
class node_base
{
public:
node_base();
virtual ~node_base();
std::vector< std::shared_ptr<node_base> > get_adjacent_nodes();
};
이제 내 노드는 다른 유형이 될 수 있습니다. 이것들은 어떻습니까?
class red_node : virtual public node_base
{
public:
red_node();
virtual ~red_node();
void get_redness();
};
class yellow_node : virtual public node_base
{
public:
yellow_node();
virtual ~yellow_node();
void set_yellowness(int);
};
지옥, 왜 이것들 중 하나는 안될까요?
class orange_node : public red_node, public yellow_node
{
public:
orange_node();
virtual ~orange_node();
void poke();
void poke_adjacent_oranges();
};
마지막 기능은 흥미 롭습니다. 작성하는 방법은 다음과 같습니다.
void orange_node::poke_adjacent_oranges()
{
auto adj_nodes = get_adjacent_nodes();
foreach(auto node, adj_nodes) {
// In this case, typeid() and static_cast might be faster
std::shared_ptr<orange_node> o_node = dynamic_cast<orange_node>(node);
if (o_node) {
o_node->poke();
}
}
}
이 모든 것이 명확하고 깨끗해 보입니다. 필요하지 않은 속성이나 메서드를 정의 할 필요가 없습니다. 기본 노드 클래스는 간결하고 의미를 유지할 수 있습니다. RTTI가 없으면 어디서부터 시작해야합니까? 기본 클래스에 node_type 속성을 추가 할 수 있습니다.
class node_base
{
public:
node_base();
virtual ~node_base();
std::vector< std::shared_ptr<node_base> > get_adjacent_nodes();
private:
std::string my_type;
};
std :: string 유형에 대한 좋은 아이디어입니까? 아닐 수도 있지만 다른 무엇을 사용할 수 있습니까? 번호를 만들어 다른 사람이 아직 사용하지 않기를 바랍니다. 또한 내 orange_node의 경우 red_node 및 yellow_node의 메소드를 사용하려면 어떻게해야합니까? 노드 당 여러 유형을 저장해야합니까? 복잡해 보입니다.
결론
이 예제는 지나치게 복잡하거나 비정상적으로 보이지 않습니다 (저는 일상 업무에서 유사한 작업을 수행하고 있습니다. 여기서 노드는 소프트웨어를 통해 제어되는 실제 하드웨어를 나타내며 무엇인지에 따라 매우 다른 작업을 수행합니다). 그러나 나는 템플릿이나 다른 방법으로 이것을 수행하는 깨끗한 방법을 알지 못할 것입니다. 나는 내 모범을 변호하는 것이 아니라 문제를 이해하려고 노력하고 있음을 유의하십시오. 위에서 링크 한 SO 답변과 Wikibooks의이 페이지 와 같은 페이지를 읽으면 RTTI를 오용하고 있음을 암시하는 것 같지만 그 이유를 알고 싶습니다.
그래서, 내 원래 질문으로 돌아가십시오. RTTI를 사용하는 것보다 '순수한 다형성'이 더 바람직한 이유는 무엇입니까?
node_base
가 라이브러리의 일부이고 사용자가 자신의 노드 유형을 만들 것이라고 제안한다는 생각을 주목하지 않습니다 . 그러면 다른 솔루션을 허용하도록 수정할 수 없으므로node_base
RTTI가 최선의 선택이 될 수 있습니다. 다른 한편으로, RTTI (및 새로운 노드 유형을 설계하는 다른 방법)를 사용하지 않고도 새 노드 유형이 훨씬 더 우아하게 맞출 수 있도록 이러한 라이브러리를 설계하는 다른 방법이 있습니다.