Java의 instanceof와 동등한 C ++


202

java와 동등한 C ++을 달성하기 위해 선호되는 방법은 무엇입니까 instanceof?


57
... 성능과 호환성을 선호
Yuval 교수 아담

7
"인스턴스-어떤 언어로"물어 보는 것이 불공평합니까?
mysticcoder

3
@mysticcoder : 나는 GT는하지만 C ++을 지원하지 않습니다, 불가리아어은 "например на"얻을
마크 K 코완

답변:


200

다음을 사용하십시오.

if(NewType* v = dynamic_cast<NewType*>(old)) {
   // old was safely casted to NewType
   v->doSomething();
}

이를 위해서는 컴파일러에 rtti 지원이 활성화되어 있어야합니다.

편집 :이 답변에 대한 좋은 의견이 있습니다!

dynamic_cast (또는 instanceof)를 사용해야 할 때마다 필요한지 여부를 스스로 확인하는 것이 좋습니다. 일반적으로 디자인이 좋지 않다는 신호입니다.

일반적인 해결 방법은 확인하려는 클래스의 특수 동작을 기본 클래스의 가상 함수에 넣거나 인터페이스를 변경하지 않고 서브 클래스에 특정 동작을 도입 할 수 있는 방문자 와 같은 것을 소개하는 것입니다 (방문자 수용 인터페이스 추가 제외) 강좌).

지적한 바와 같이 dynamic_cast는 무료로 제공되지 않습니다. 대부분의 경우를 처리하는 단순하고 일관되게 수행되는 해킹은 기본적으로 클래스가 가질 수있는 모든 가능한 유형을 나타내는 열거 형을 추가하고 올바른 유형을 가지고 있는지 확인하는 것입니다.

if(old->getType() == BOX) {
   Box* box = static_cast<Box*>(old);
   // Do something box specific
}

이것은 좋은 디자인은 아니지만 해결 방법이 될 수 있으며 비용은 가상 함수 호출에 불과합니다. RTTI의 활성화 여부에 관계없이 작동합니다.

이 접근 방식은 여러 수준의 상속을 지원하지 않으므로주의하지 않으면 다음과 같은 코드로 끝날 수 있습니다.

// Here we have a SpecialBox class that inherits Box, since it has its own type
// we must check for both BOX or SPECIAL_BOX
if(old->getType() == BOX || old->getType() == SPECIAL_BOX) {
   Box* box = static_cast<Box*>(old);
   // Do something box specific
}

4
"instanceof"점검을하는 경우가 일반적입니다.
Laserallan

7
instanceof를 사용해야하는 경우 대부분의 경우 디자인에 문제가 있습니다.
mslot

24
dynamic_cast는 비용이 많이 드는 작업이라는 것을 잊지 마십시오.
Klaim

13
동적 유형 테스트의 합리적인 사용에 대한 많은 예가 있습니다. 일반적으로 선호되지는 않지만 장소가 있습니다. (그렇지 않으면 C ++, Java, Python 등 모든 주요 OO- 언어에 왜 또는 이와 동등한 내용이 표시됩니까?)
Paul Draper

2
서로 다르게 처리 할 필요가 없다면 IOException 수준에서 둘 다 잡을 것입니다. 그것들을 다른 방식으로 처리해야 할 경우 각 예외에 대해 catch 블록을 추가합니다.
mslot

37

당신이하고 싶은 것에 따라 당신은 이것을 할 수 있습니다 :

template<typename Base, typename T>
inline bool instanceof(const T*) {
    return std::is_base_of<Base, T>::value;
}

사용하다:

if (instanceof<BaseClass>(ptr)) { ... }

그러나 이것은 순수하게 컴파일러에 알려진 유형에서 작동합니다.

편집하다:

이 코드는 다형성 포인터에 대해 작동해야합니다.

template<typename Base, typename T>
inline bool instanceof(const T *ptr) {
    return dynamic_cast<const Base*>(ptr) != nullptr;
}

예 : http://cpp.sh/6qir


우아하고 잘된 솔루션. +1 그러나 올바른 포인터를 얻도록주의하십시오. 다형성 포인터에는 유효하지 않습니까?
Adrian Maire 2016 년

이 함수를 사용할 때 포인터를 지연 시키면 어떻게 되나요? 그러면 다형성 포인터에서 작동합니까?
mark.kedzierski

이것은 컴파일러에게 알려진 유형에서만 작동합니다. 당신이 derefernece 여부에 관계없이 다형성 포인터와 함께 작동하지 않습니다. 이 경우에는 효과가있는 것을 추가하겠습니다.
panzi

2
포인터 대신 참조를 사용하는이 메소드의 버전을 작성하도록 예제를 수정했습니다. cpp.sh/8owv
Sri Harsha Chilakapati

동적 캐스트의 대상 유형이 "const"인 이유는 무엇입니까?
user1056903

7

dynamic_cast를 사용하지 않는 구현 인스턴스

이 질문은 오늘날에도 여전히 관련이 있다고 생각합니다. C ++ 11 표준을 instanceof사용 dynamic_cast하면 다음과 같이 사용하지 않고도 함수 를 구현할 수 있습니다 .

if (dynamic_cast<B*>(aPtr) != nullptr) {
  // aPtr is instance of B
} else {
  // aPtr is NOT instance of B
}

그러나 여전히 RTTI지원에 의존하고 있습니다. 그래서 여기에 일부 매크로 및 메타 프로그래밍 매직에 따라이 문제에 대한 해결책이 있습니다. 유일한 단점은이 접근 방식이 다중 상속 에는 적용 되지 않는다는 것입니다 .

InstanceOfMacros.h

#include <set>
#include <tuple>
#include <typeindex>

#define _EMPTY_BASE_TYPE_DECL() using BaseTypes = std::tuple<>;
#define _BASE_TYPE_DECL(Class, BaseClass) \
  using BaseTypes = decltype(std::tuple_cat(std::tuple<BaseClass>(), Class::BaseTypes()));
#define _INSTANCE_OF_DECL_BODY(Class)                                 \
  static const std::set<std::type_index> baseTypeContainer;           \
  virtual bool instanceOfHelper(const std::type_index &_tidx) {       \
    if (std::type_index(typeid(ThisType)) == _tidx) return true;      \
    if (std::tuple_size<BaseTypes>::value == 0) return false;         \
    return baseTypeContainer.find(_tidx) != baseTypeContainer.end();  \
  }                                                                   \
  template <typename... T>                                            \
  static std::set<std::type_index> getTypeIndexes(std::tuple<T...>) { \
    return std::set<std::type_index>{std::type_index(typeid(T))...};  \
  }

#define INSTANCE_OF_SUB_DECL(Class, BaseClass) \
 protected:                                    \
  using ThisType = Class;                      \
  _BASE_TYPE_DECL(Class, BaseClass)            \
  _INSTANCE_OF_DECL_BODY(Class)

#define INSTANCE_OF_BASE_DECL(Class)                                                    \
 protected:                                                                             \
  using ThisType = Class;                                                               \
  _EMPTY_BASE_TYPE_DECL()                                                               \
  _INSTANCE_OF_DECL_BODY(Class)                                                         \
 public:                                                                                \
  template <typename Of>                                                                \
  typename std::enable_if<std::is_base_of<Class, Of>::value, bool>::type instanceOf() { \
    return instanceOfHelper(std::type_index(typeid(Of)));                               \
  }

#define INSTANCE_OF_IMPL(Class) \
  const std::set<std::type_index> Class::baseTypeContainer = Class::getTypeIndexes(Class::BaseTypes());

데모

그런 다음이 물건 을 다음과 같이 사용할 수 있습니다 ( 주의 ).

DemoClassHierarchy.hpp *

#include "InstanceOfMacros.h"

struct A {
  virtual ~A() {}
  INSTANCE_OF_BASE_DECL(A)
};
INSTANCE_OF_IMPL(A)

struct B : public A {
  virtual ~B() {}
  INSTANCE_OF_SUB_DECL(B, A)
};
INSTANCE_OF_IMPL(B)

struct C : public A {
  virtual ~C() {}
  INSTANCE_OF_SUB_DECL(C, A)
};
INSTANCE_OF_IMPL(C)

struct D : public C {
  virtual ~D() {}
  INSTANCE_OF_SUB_DECL(D, C)
};
INSTANCE_OF_IMPL(D)

다음 코드는 기초가 올바른 동작인지 확인하기위한 작은 데모입니다.

InstanceOfDemo.cpp

#include <iostream>
#include <memory>
#include "DemoClassHierarchy.hpp"

int main() {
  A *a2aPtr = new A;
  A *a2bPtr = new B;
  std::shared_ptr<A> a2cPtr(new C);
  C *c2dPtr = new D;
  std::unique_ptr<A> a2dPtr(new D);

  std::cout << "a2aPtr->instanceOf<A>(): expected=1, value=" << a2aPtr->instanceOf<A>() << std::endl;
  std::cout << "a2aPtr->instanceOf<B>(): expected=0, value=" << a2aPtr->instanceOf<B>() << std::endl;
  std::cout << "a2aPtr->instanceOf<C>(): expected=0, value=" << a2aPtr->instanceOf<C>() << std::endl;
  std::cout << "a2aPtr->instanceOf<D>(): expected=0, value=" << a2aPtr->instanceOf<D>() << std::endl;
  std::cout << std::endl;
  std::cout << "a2bPtr->instanceOf<A>(): expected=1, value=" << a2bPtr->instanceOf<A>() << std::endl;
  std::cout << "a2bPtr->instanceOf<B>(): expected=1, value=" << a2bPtr->instanceOf<B>() << std::endl;
  std::cout << "a2bPtr->instanceOf<C>(): expected=0, value=" << a2bPtr->instanceOf<C>() << std::endl;
  std::cout << "a2bPtr->instanceOf<D>(): expected=0, value=" << a2bPtr->instanceOf<D>() << std::endl;
  std::cout << std::endl;
  std::cout << "a2cPtr->instanceOf<A>(): expected=1, value=" << a2cPtr->instanceOf<A>() << std::endl;
  std::cout << "a2cPtr->instanceOf<B>(): expected=0, value=" << a2cPtr->instanceOf<B>() << std::endl;
  std::cout << "a2cPtr->instanceOf<C>(): expected=1, value=" << a2cPtr->instanceOf<C>() << std::endl;
  std::cout << "a2cPtr->instanceOf<D>(): expected=0, value=" << a2cPtr->instanceOf<D>() << std::endl;
  std::cout << std::endl;
  std::cout << "c2dPtr->instanceOf<A>(): expected=1, value=" << c2dPtr->instanceOf<A>() << std::endl;
  std::cout << "c2dPtr->instanceOf<B>(): expected=0, value=" << c2dPtr->instanceOf<B>() << std::endl;
  std::cout << "c2dPtr->instanceOf<C>(): expected=1, value=" << c2dPtr->instanceOf<C>() << std::endl;
  std::cout << "c2dPtr->instanceOf<D>(): expected=1, value=" << c2dPtr->instanceOf<D>() << std::endl;
  std::cout << std::endl;
  std::cout << "a2dPtr->instanceOf<A>(): expected=1, value=" << a2dPtr->instanceOf<A>() << std::endl;
  std::cout << "a2dPtr->instanceOf<B>(): expected=0, value=" << a2dPtr->instanceOf<B>() << std::endl;
  std::cout << "a2dPtr->instanceOf<C>(): expected=1, value=" << a2dPtr->instanceOf<C>() << std::endl;
  std::cout << "a2dPtr->instanceOf<D>(): expected=1, value=" << a2dPtr->instanceOf<D>() << std::endl;

  delete a2aPtr;
  delete a2bPtr;
  delete c2dPtr;

  return 0;
}

산출:

a2aPtr->instanceOf<A>(): expected=1, value=1
a2aPtr->instanceOf<B>(): expected=0, value=0
a2aPtr->instanceOf<C>(): expected=0, value=0
a2aPtr->instanceOf<D>(): expected=0, value=0

a2bPtr->instanceOf<A>(): expected=1, value=1
a2bPtr->instanceOf<B>(): expected=1, value=1
a2bPtr->instanceOf<C>(): expected=0, value=0
a2bPtr->instanceOf<D>(): expected=0, value=0

a2cPtr->instanceOf<A>(): expected=1, value=1
a2cPtr->instanceOf<B>(): expected=0, value=0
a2cPtr->instanceOf<C>(): expected=1, value=1
a2cPtr->instanceOf<D>(): expected=0, value=0

c2dPtr->instanceOf<A>(): expected=1, value=1
c2dPtr->instanceOf<B>(): expected=0, value=0
c2dPtr->instanceOf<C>(): expected=1, value=1
c2dPtr->instanceOf<D>(): expected=1, value=1

a2dPtr->instanceOf<A>(): expected=1, value=1
a2dPtr->instanceOf<B>(): expected=0, value=0
a2dPtr->instanceOf<C>(): expected=1, value=1
a2dPtr->instanceOf<D>(): expected=1, value=1

공연

현재 가장 흥미로운 질문은이 악의적 인 것들이 dynamic_cast . 따라서 저는 매우 기본적인 성능 측정 앱을 작성했습니다.

InstanceOfPerformance.cpp

#include <chrono>
#include <iostream>
#include <string>
#include "DemoClassHierarchy.hpp"

template <typename Base, typename Derived, typename Duration>
Duration instanceOfMeasurement(unsigned _loopCycles) {
  auto start = std::chrono::high_resolution_clock::now();
  volatile bool isInstanceOf = false;
  for (unsigned i = 0; i < _loopCycles; ++i) {
    Base *ptr = new Derived;
    isInstanceOf = ptr->template instanceOf<Derived>();
    delete ptr;
  }
  auto end = std::chrono::high_resolution_clock::now();
  return std::chrono::duration_cast<Duration>(end - start);
}

template <typename Base, typename Derived, typename Duration>
Duration dynamicCastMeasurement(unsigned _loopCycles) {
  auto start = std::chrono::high_resolution_clock::now();
  volatile bool isInstanceOf = false;
  for (unsigned i = 0; i < _loopCycles; ++i) {
    Base *ptr = new Derived;
    isInstanceOf = dynamic_cast<Derived *>(ptr) != nullptr;
    delete ptr;
  }
  auto end = std::chrono::high_resolution_clock::now();
  return std::chrono::duration_cast<Duration>(end - start);
}

int main() {
  unsigned testCycles = 10000000;
  std::string unit = " us";
  using DType = std::chrono::microseconds;

  std::cout << "InstanceOf performance(A->D)  : " << instanceOfMeasurement<A, D, DType>(testCycles).count() << unit
            << std::endl;
  std::cout << "InstanceOf performance(A->C)  : " << instanceOfMeasurement<A, C, DType>(testCycles).count() << unit
            << std::endl;
  std::cout << "InstanceOf performance(A->B)  : " << instanceOfMeasurement<A, B, DType>(testCycles).count() << unit
            << std::endl;
  std::cout << "InstanceOf performance(A->A)  : " << instanceOfMeasurement<A, A, DType>(testCycles).count() << unit
            << "\n"
            << std::endl;
  std::cout << "DynamicCast performance(A->D) : " << dynamicCastMeasurement<A, D, DType>(testCycles).count() << unit
            << std::endl;
  std::cout << "DynamicCast performance(A->C) : " << dynamicCastMeasurement<A, C, DType>(testCycles).count() << unit
            << std::endl;
  std::cout << "DynamicCast performance(A->B) : " << dynamicCastMeasurement<A, B, DType>(testCycles).count() << unit
            << std::endl;
  std::cout << "DynamicCast performance(A->A) : " << dynamicCastMeasurement<A, A, DType>(testCycles).count() << unit
            << "\n"
            << std::endl;
  return 0;
}

결과는 다양하며 본질적으로 컴파일러 최적화 정도를 기반으로합니다. g++ -std=c++11 -O0 -o instanceof-performance InstanceOfPerformance.cpp로컬 컴퓨터의 출력을 사용하여 성능 측정 프로그램을 컴파일하는 방법 은 다음과 같습니다.

InstanceOf performance(A->D)  : 699638 us
InstanceOf performance(A->C)  : 642157 us
InstanceOf performance(A->B)  : 671399 us
InstanceOf performance(A->A)  : 626193 us

DynamicCast performance(A->D) : 754937 us
DynamicCast performance(A->C) : 706766 us
DynamicCast performance(A->B) : 751353 us
DynamicCast performance(A->A) : 676853 us

흠,이 결과는 새로운 접근 방식이 접근 방식에 비해 훨씬 빠르지 않다는 타이밍을 보여주기 때문에 매우 냉정했습니다 dynamic_cast. 포인터 A가의 인스턴스 인지 테스트하는 특수 테스트 사례의 경우 훨씬 덜 효율적 입니다 A. 그러나 컴파일러 otpimization을 사용하여 바이너리를 조정하면 조수가 바뀝니다. 각각의 컴파일러 명령은 g++ -std=c++11 -O3 -o instanceof-performance InstanceOfPerformance.cpp입니다. 내 로컬 컴퓨터의 결과는 놀랍습니다.

InstanceOf performance(A->D)  : 3035 us
InstanceOf performance(A->C)  : 5030 us
InstanceOf performance(A->B)  : 5250 us
InstanceOf performance(A->A)  : 3021 us

DynamicCast performance(A->D) : 666903 us
DynamicCast performance(A->C) : 698567 us
DynamicCast performance(A->B) : 727368 us
DynamicCast performance(A->A) : 3098 us

다중 상속에 의존하지 않고 좋은 오래된 C 매크로, RTTI 및 템플릿 메타 프로그래밍에 반대하지 않고 클래스 계층의 클래스에 작은 명령을 추가하기에는 너무 게으르지 않은 경우이 접근법은 응용 프로그램을 약간 향상시킬 수 있습니다 종종 포인터의 인스턴스를 확인하면 성능과 관련하여. 그러나주의해서 사용하십시오 . 이 방법의 정확성에 대한 보증은 없습니다.

참고 : 모든 데모는 clang (Apple LLVM version 9.0.0 (clang-900.0.39.2))2012 년 MacBook Pro Mid의 macOS Sierra에서 사용 되었습니다.

편집 : 또한를 사용하여 Linux 컴퓨터에서 성능을 테스트했습니다 gcc (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609. 이 플랫폼에서 성능 이점은 clang을 사용하는 macOs만큼 중요하지 않았습니다.

출력 (컴파일러 최적화없이) :

InstanceOf performance(A->D)  : 390768 us
InstanceOf performance(A->C)  : 333994 us
InstanceOf performance(A->B)  : 334596 us
InstanceOf performance(A->A)  : 300959 us

DynamicCast performance(A->D) : 331942 us
DynamicCast performance(A->C) : 303715 us
DynamicCast performance(A->B) : 400262 us
DynamicCast performance(A->A) : 324942 us

출력 (컴파일러 최적화 사용) :

InstanceOf performance(A->D)  : 209501 us
InstanceOf performance(A->C)  : 208727 us
InstanceOf performance(A->B)  : 207815 us
InstanceOf performance(A->A)  : 197953 us

DynamicCast performance(A->D) : 259417 us
DynamicCast performance(A->C) : 256203 us
DynamicCast performance(A->B) : 261202 us
DynamicCast performance(A->A) : 193535 us

잘 생각해보십시오! 시간을 내 주셔서 감사합니다. 이것은 흥미로운 읽을 거리였습니다.
Eric

0

dynamic_cast비효율적 인 것으로 알려져 있습니다. 그것은 상속 계층 구조를 통과하고, 그 것이다 유일한 당신이 상속의 여러 수준이있는 경우 솔루션 및 객체가 유형 계층에있는 유형 중 하나의 인스턴스가 있는지 확인해야합니다.

그러나 instanceof객체의 형식이 객체가 정확히 지정한 유형인지 확인하고 필요에 따라 충분하다면 아래 기능이 훨씬 효율적입니다.

template<typename T, typename K>
inline bool isType(const K &k) {
    return typeid(T).hash_code() == typeid(k).hash_code();
}

위 함수를 호출하는 방법에 대한 예는 다음과 같습니다.

DerivedA k;
Base *p = &k;

cout << boolalpha << isType<DerivedA>(*p) << endl;  // true
cout << boolalpha << isType<DerivedB>(*p) << endl;  // false

템플릿 유형 A(확인하려는 유형)을 지정 하고 테스트하려는 객체를 인수로 전달합니다 (템플릿 유형 K이 유추 될 것임).


표준은 서로 다른 유형에 대해 hash_code가 고유하지 않아도되므로 신뢰할 수 없습니다.
mattnz

2
typeid (T) 자체가 동등성과 비교할 수 없으므로 해시 코드에 의존 할 필요가 없습니까?
Paul Stelian

-5
#include <iostream.h>
#include<typeinfo.h>

template<class T>
void fun(T a)
{
  if(typeid(T) == typeid(int))
  {
     //Do something
     cout<<"int";
  }
  else if(typeid(T) == typeid(float))
  {
     //Do Something else
     cout<<"float";
  }
}

void main()
 {
      fun(23);
      fun(90.67f);
 }

1
이것은 정말 나쁜 예입니다. 왜 과부하를 사용하지 않습니까?
user1095108

11
주요 문제는 질문에 대답하지 못한다는 것입니다. instanceof동적 유형을 쿼리하지만이 답변에서 동적 및 정적 유형은 항상 일치합니다.
MSalters

@HHH 당신이 대답하는 것은 질문되는 질문에서 벗어나는 길입니다!
프로그래머

-11

이것은 GCC 컴파일러와 함께 Code :: Blocks IDE를 사용하여 나에게 완벽하게 작동했습니다.

#include<iostream>
#include<typeinfo>
#include<iomanip>
#define SIZE 20
using namespace std;

class Publication
{
protected:
    char title[SIZE];
    int price;

public:
    Publication()
    {
        cout<<endl<<" Enter title of media : ";
        cin>>title;

        cout<<endl<<" Enter price of media : ";
        cin>>price;
    }

    virtual void show()=0;
};

class Book : public Publication
{
    int pages;

public:
    Book()
    {
        cout<<endl<<" Enter number of pages : ";
        cin>>pages;
    }

    void show()
    {
        cout<<endl<<setw(12)<<left<<" Book Title"<<": "<<title;
        cout<<endl<<setw(12)<<left<<" Price"<<": "<<price;
        cout<<endl<<setw(12)<<left<<" Pages"<<": "<<pages;
        cout<<endl<<" ----------------------------------------";
    }
};

class Tape : public Publication
{
    int duration;

public:
    Tape()
    {
        cout<<endl<<" Enter duration in minute : ";
        cin>>duration;
    }

    void show()
    {
        cout<<endl<<setw(10)<<left<<" Tape Title"<<": "<<title;
        cout<<endl<<setw(10)<<left<<" Price"<<": "<<price;
        cout<<endl<<setw(10)<<left<<" Duration"<<": "<<duration<<" minutes";
        cout<<endl<<" ----------------------------------------";
    }
};
int main()
{
    int n, i, type;

    cout<<endl<<" Enter number of media : ";
    cin>>n;

    Publication **p = new Publication*[n];
    cout<<endl<<" Enter "<<n<<" media details : ";

    for(i=0;i<n;i++)
    {
        cout<<endl<<" Select Media Type [ 1 - Book / 2 - Tape ] ";
        cin>>type;

        if ( type == 1 )
        {
            p[i] = new Book();
        }
        else
        if ( type == 2 )
        {
            p[i] = new Tape();
        }
        else
        {
            i--;
            cout<<endl<<" Invalid type. You have to Re-enter choice";
        }
    }

    for(i=0;i<n;i++)
    {
        if ( typeid(Book) == typeid(*p[i]) )
        {
            p[i]->show();
        }
    }

    return 0;
}

1
@programmer @pgp를 부르겠다고 생각합니다. 단순히 그의 코드 형식을 고쳤습니다. 또한, 그의 대답은 기본적으로 "사용 것 같다 typeid"... 같은 표준 : 예있는 type_info가 동일한 유형의 유형 ID 발현의 모든 평가에 의해 참조 될 것이라는 보장이 없다 잘못 동안 ( " assert(typeid(A) == typeid(A)); /* not guaranteed */참조" cppreference.com )은 최소한의 실례를 제시하지 않았기 때문에 도움이되지 않으면 적어도 질문에 대답하려고 시도했음을 나타냅니다.
Andres Riofrio
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.