C ++에서 객체 유형 찾기


147

클래스 A와 클래스 B를 상속받는 다른 클래스가 있습니다 .A 유형의 객체를 매개 변수로 허용하는 함수를 재정의하고 있으므로 A를 수락해야합니다. 그러나 나중에 B 만있는 함수를 호출합니다. 따라서 전달 된 객체가 B 유형이 아닌 경우 false를 반환하고 진행하지 않고 싶습니다.

객체가 함수에 전달한 유형을 찾는 가장 좋은 방법은 무엇입니까?

답변:


162

dynamic_cast가 트릭을 수행해야합니다

TYPE& dynamic_cast<TYPE&> (object);
TYPE* dynamic_cast<TYPE*> (object);

dynamic_cast키워드는 캐스트의 유효성을 보장하기 위해 런타임 검사를 수행, 다른 하나 개의 포인터 또는 참조 유형에서 데이텀을 캐스팅합니다.

실제 객체 유형이 아닌 유형에 대한 포인터를 캐스트하려고하면 캐스트 결과는 NULL입니다. 실제 객체 유형이 아닌 유형을 참조하기 위해 캐스트하려고하면 캐스트에서 bad_cast예외 가 발생합니다.

dynamic_cast가 작동하도록 Base 클래스에 하나 이상의 가상 함수가 있는지 확인하십시오.

위키 백과 주제 런타임 유형 정보

RTTI는 다형성 클래스에만 사용할 수 있습니다. 즉, 하나 이상의 가상 메소드가 있습니다. 실제로, 기본 클래스에는 파생 클래스의 객체가 기본 포인터에서 삭제 된 경우 적절한 정리를 수행 할 수 있도록 가상 소멸자가 있어야하므로 제한이 없습니다.


1
dynamic_cast가 작동하려면 Base 클래스에 가상 함수가 있어야 함을 의미합니다. 그것은 내가 생각하기에 중요한 것 같습니다.
GiCo

3
확인 결과 : RTTI (Run-Time Type Information)는 다형성 클래스에만 사용할 수 있습니다. 즉, 하나 이상의 가상 메소드가 있습니다. dynamic_cast 및 typeid에는 RTTI가 필요합니다.
GiCo

하지 않습니다 dynamic_cast컨버터블하지 않을 경우 던져? 던지는 일없이 할 수있는 방법이 있습니까?
jww

A* aptr = dynamic_cast<A*>(ptr);// 이런 식으로되어 있지
않은가

이로 캐스팅 된 POD에 대해서도 작동합니까 uint8_t*? 즉, 나는 그것을 확인할 수 있습니다 uint32_t* x = dynamic_cast<uint32_t*>(p), 어디 p있다 uint8_t*? (정상 위반에 대한 테스트를 찾으려고합니다).
jww

157

동적 캐스트는 문제 설명에 가장 적합하지만 다음과 같이 클래스 유형을 찾을 수 있다고 덧붙이고 싶습니다.

#include <typeinfo>

...
string s = typeid(YourClass).name()

4
물체가 무엇인지 정말로 모른다면 좋습니다. 받아 들여진 대답은 당신이한다고 가정합니다.
unludo

4
@xus 예. 그것은 std 헤더의 일부입니다
Amey Jah

8
나는 보이지 않는 방법에 대해 설명합니다. 유형 ID 이름은 유용하지 않아도되며 구현 정의됩니다.
구두

3
여기서 가장 흥미로운 것은 : 같은 클래스의 인스턴스 이름이 같을 필요는 없습니다. 그러나 typeid 자체는 같은 클래스의 인스턴스에 대해 동일하게 비교해야합니다. stackoverflow.com/questions/1986418/typeid-versus-typeof-in-c
FourtyTwo

1
참고 gcc는 예를 들어 확대 된 이름을 반환합니다 11MyClass. 엉키지 않게하려면에서 ABI 확장 라이브러리를 사용할 수 있습니다 cxxabi.h. 이것은 당신 abi::__cxa_demangle에게 실제 이름을 줄 것입니다
David G

27

이것을 RTTI 라고 부르지 만, 타입을 찾아서 그것을 기반으로 특별한 작업을 수행하면 코드가 더 부서지기 쉽기 때문에 디자인을 다시 생각하고 싶을 것입니다.


4
진실. 불행히도 기존 프로젝트를 진행 중이므로 디자인이나 클래스 A를 변경할 수는 없습니다.
lemnisca

11

완료하기 위해 Robocide에서 빌드를 빌드하고 typeidname ()을 사용하지 않고 단독으로 사용할 수 있음을 지적합니다 .

#include <typeinfo>
#include <iostream>

using namespace std;

class A {
public:
    virtual ~A() = default; // We're not polymorphic unless we
                            // have a virtual function.
};
class B : public A { } ;
class C : public A { } ;

int
main(int argc, char* argv[])
{
    B b;
    A& a = b;

    cout << "a is B: " << boolalpha << (typeid(a) == typeid(B)) << endl;
    cout << "a is C: " << boolalpha << (typeid(a) == typeid(C)) << endl;
    cout << "b is B: " << boolalpha << (typeid(b) == typeid(B)) << endl;
    cout << "b is A: " << boolalpha << (typeid(b) == typeid(A)) << endl;
    cout << "b is C: " << boolalpha << (typeid(b) == typeid(C)) << endl;
}

산출:

a is B: true
a is C: false
b is B: true
b is A: false
b is C: false

9

아마도 개체에 ID "태그"를 포함시키고 클래스 A의 개체와 클래스 B의 개체를 구별하는 데 사용합니다.

그러나 이것은 디자인의 결함을 보여줍니다. 이상적으로 A에없는 B의 메소드는 A의 일부이지만 비워 두어야하고 B가이를 덮어 씁니다. 이것은 클래스 별 코드를 없애고 OOP의 정신에 더 가깝습니다.



4

당신의 클래스는 다형성이 아니기 때문입니다. 시험:

struct BaseClas { int base; virtual ~BaseClas(){} };
class Derived1 : public BaseClas { int derived1; };

이제 BaseClas다형성입니다. 구조체의 멤버는 기본적으로 공개이기 때문에 클래스를 구조체로 변경했습니다.


3

설명이 약간 혼란 스럽다.

일반적으로 일부 C ++ 구현에는 메커니즘이 있지만 유형에 대해 묻지 않아도됩니다. 대신 A에 대한 포인터에서 dynamic_cast를 수행해야합니다.이 작업은 런타임에 A에 대한 포인터의 실제 내용을 확인하는 것입니다. B가있는 경우 B에 대한 포인터를 얻습니다. 그렇지 않으면 예외가 발생하거나 null이 발생합니다.


1
실패한 참조 캐스트를 수행하는 경우 에만 예외 가 발생합니다. 즉 dynamic_cast <T &> (t)입니다. 실패한 포인터 캐스트는 NULL을 반환합니다. 즉 dynamic_cast <T *> (t)
AlfaZulu

그래, 나는 그것을 더 잘 분명히 했어야했다. 감사. 값이 아닌 참조로 사용되는 C 유형에 설명이라는 단어가 있었으면 좋겠습니다.
Uri

3

다른 사람들이 지적했듯이 dynamic_cast를 사용할 수 있습니다. 그러나 일반적으로 작업중 인 파생 클래스의 유형을 찾기 위해 dynamic_cast를 사용하면 디자인이 잘못되었음을 나타냅니다. A의 포인터를 매개 변수로 사용하는 함수를 재정의하는 경우 클래스 A의 메서드 / 데이터 자체에서 작업 할 수 있어야하며 클래스 B의 데이터에 의존해서는 안됩니다. 경우에 따라 재정의하는 대신 작성하는 메소드가 클래스 B에서만 작동하는지 확인한 다음 클래스 B에 새 메소드를 작성해야합니다.


1

과부하 된 기능을 사용하십시오. dynamic_cast 또는 RTTI 지원이 필요하지 않습니다.

class A {};
class B : public A {};

class Foo {
public:
    void Bar(A& a) {
        // do something
    }
    void Bar(B& b) {
        Bar(static_cast<A&>(b));
        // do B specific stuff
    }
};

원래 질문에서 바로 : "나중에 B 만 가지고있는 함수를 호출합니다."-그런 경우 과부하는 어떻게 작동합니까?
Marcin Gil

A를 사용하여 Bar를 호출하면 B가 발생하지 않습니다. B를 사용하여 Bar를 호출하면 B에만 존재하는 메소드를 호출 할 수 있습니다. 원래 질문을 읽습니까? Bar는 "A 타입의 객체를 매개 변수로 받아들이는 함수를 재정의하고있다"
jmucchiello

7
이것은 질문자가 사용하고 있다고 생각되는 동적 다형성에서는 작동하지 않습니다. C ++은 컴파일 타임 유형에 대해서만 매개 변수의 런타임 클래스를 기반으로 오버로드를 선택할 수 없습니다.
Steve Jessop

1

부스트 라이브러리에 액세스 할 수 있다면 type_id_with_cvr () 함수가 필요할 것입니다 .const , volatile, & 및 && 수정자를 제거하지 않고도 데이터 유형을 제공 할 수 있습니다 . 다음은 C ++ 11의 간단한 예입니다.

#include <iostream>
#include <boost/type_index.hpp>

int a;
int& ff() 
{
    return a;
}

int main() {
    ff() = 10;
    using boost::typeindex::type_id_with_cvr;
    std::cout << type_id_with_cvr<int&>().pretty_name() << std::endl;
    std::cout << type_id_with_cvr<decltype(ff())>().pretty_name() << std::endl;
    std::cout << typeid(ff()).name() << std::endl;
}

이것이 유용하기를 바랍니다.

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