programing

C++는 Java의 인스턴스와 동등합니다.

randomtip 2022. 7. 14. 21:21
반응형

C++는 Java의 인스턴스와 동등합니다.

자바++와 한 C를 실현하기 입니까?instanceof

사용 방법:

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

이를 위해서는 컴파일러가 rtti 지원을 활성화해야 합니다.

편집: 이 답변에 대해 좋은 코멘트를 받았습니다!

dynamic_cast(또는 instance)를 사용해야 할 때마다 이것이 필요한지 자문해 보는 것이 좋습니다.그것은 일반적으로 디자인이 좋지 않다는 표시이다.

일반적인 회피책은 체크하는 클래스의 특별한 동작을 기본 클래스의 가상 함수에 넣거나 인터페이스를 변경하지 않고 서브 클래스의 특정 동작을 도입하는 입니다(물론 방문자 수용 인터페이스 추가 제외).

지적하신 바와 같이 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
}

원하는 작업에 따라 다음을 수행할 수 있습니다.

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

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
}

당신은 지지 but but but but but에 의존하고 있다.RTTI지지하다.매크로와 메타프로그래밍 Magic에 따라 이 문제에 대한 해결책을 제시하겠습니다.유일한 단점은 이 방법이 다중 상속에는 적용되지 않는다는 것입니다.

Instance Of Macros.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());

데모

그 후, 다음과 같이 사용할 수 있습니다(주의).

데모 클래스Hierarchy.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)

다음 코드는 기본적인 올바른 동작을 확인하기 위한 작은 데모를 보여 줍니다.

Instance Of Demo.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그래서 기본적인 퍼포먼스 측정 앱을 만들었습니다.

Instance Of Performance.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

Mhm, 이 결과는 매우 냉정했습니다.타이밍을 보면 새로운 접근법이 다른 접근법에 비해 그다지 빠르지 않다는 것을 알 수 있기 때문입니다.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 매크로, RTI 및 템플릿 메타프로그래밍에 반대하지 않으며 클래스 계층의 클래스에 작은 명령을 추가하는 것도 귀찮지 않은 경우, 이 접근법은 종종 인스턴스 체크로 끝나는 경우 응용 프로그램을 약간 향상시킬 수 있습니다.포인터입니다.하지만 주의해서 사용하세요.이 방법이 올바르다는 보장은 없습니다.

주의: 모든 데모는 다음과 같이 컴파일되었습니다.clang (Apple LLVM version 9.0.0 (clang-900.0.39.2))MacBook Pro Mid 2012의 MacOS Sierra에서 사용할 수 있습니다.

편집: Linux 머신의 퍼포먼스도 테스트했습니다.gcc (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609이 플랫폼에서는 clang이 있는 macO에서는 성능상의 이점이 그다지 크지 않았습니다.

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

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

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추측할 수 있습니다.)

#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);
 }

언급URL : https://stackoverflow.com/questions/500493/c-equivalent-of-javas-instanceof

반응형