Эквивалент С++ экземпляра

Каков предпочтительный метод для получения эквивалента С++ instanceof?

Ответ 1

Попробуйте использовать:

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

Это требует, чтобы ваш компилятор поддерживал поддержку rtti.

EDIT: У меня были хорошие комментарии к этому ответу!

Каждый раз, когда вам нужно использовать dynamic_cast (или instanceof), вам лучше спросить себя, нужна ли эта вещь. Это вообще признак плохого дизайна.

Типичные обходные пути - это введение специального поведения для класса, который вы проверяете, в виртуальную функцию в базовом классе или, возможно, введение чего-то вроде visitor, где вы можете ввести конкретное поведение для подклассов без изменения интерфейса (за исключением, конечно, добавления интерфейса приемки посетителей).

Как указано, 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
}

Ответ 2

В зависимости от того, что вы хотите сделать, вы можете сделать это:

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

Использование:

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

Однако это чисто действует на типы, известные компилятору.

Edit:

Этот код должен работать для полиморфных указателей:

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

Пример: http://cpp.sh/6qir

Ответ 3

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).

Ответ 4

Экземпляр реализации без dynamic_cast

Я думаю, что этот вопрос по-прежнему актуальен и сегодня. Используя стандарт С++ 11, вы теперь можете реализовать функцию instanceof без использования dynamic_cast следующим образом:

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

Но вы по-прежнему полагаетесь на поддержку RTTI. Итак, вот мое решение для этой проблемы в зависимости от некоторых макросов и метапрограммирования Magic. Единственным недостатком imho является то, что этот подход делает не работу для множественного наследования.

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());

Demo

Затем вы можете использовать этот материал (с осторожностью) следующим образом:

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

Mhm, этот результат был очень отрезвляющим, поскольку тайминги демонстрируют, что новый подход не намного быстрее по сравнению с подходом dynamic_cast. Это еще менее эффективно для специального тестового примера, который проверяет, является ли указатель A экземпляром A. НО, прилив поворачивается, настраивая наш двоичный файл, используя отпиляцию компилятора. Соответствующая команда компилятора 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)) под MacOS Sierra на MacBook Pro в середине 2012 года.

Edit: Я также тестировал производительность на машине Linux с помощью gcc (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609. На этой платформе преимущество в производительности было не столь значительным, как на macOs с clang.

Выход (без оптимизации компилятора):

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

Ответ 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);
 }

Ответ 6

Это сработало для меня с помощью Code:: Blocks IDE с GCC complier

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