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

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

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

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

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

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

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

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

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

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

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

 template inline bool instanceof(const T*) { return std::is_base_of::value; } 

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

 if (instanceof(ptr)) { ... } 

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

Редактировать:

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

 template inline bool instanceof(const T *ptr) { return dynamic_cast(ptr) != nullptr; } 

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

dynamic_cast как известно, неэффективен. Он проходит иерархию наследования, и это единственное решение, если у вас есть несколько уровней наследования, и вам нужно проверить, является ли объект экземпляром любого из типов в его иерархии типов.

Но если более ограниченная форма instanceof которая проверяет, является ли объект точно указанным вами типом, достаточно для ваших нужд, функция ниже была бы намного более эффективной:

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

Ниже приведен пример того, как вы вызываете функцию выше:

 DerivedA k; Base *p = &k; cout << boolalpha << isType(*p) << endl; // true cout << boolalpha << isType(*p) << endl; // false 

Вы должны указать тип шаблона A (как тип, который вы проверяете), и передать объект, который хотите проверить, в качестве аргумента (из которого будет выведен тип шаблона K ).

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

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

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

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

InstanceOfMacros.h

 #include  #include  #include  #define _EMPTY_BASE_TYPE_DECL() using BaseTypes = std::tuple<>; #define _BASE_TYPE_DECL(Class, BaseClass) \ using BaseTypes = decltype(std::tuple_cat(std::tuple(), Class::BaseTypes())); #define _INSTANCE_OF_DECL_BODY(Class) \ static const std::set baseTypeContainer; \ virtual bool instanceOfHelper(const std::type_index &_tidx) { \ if (std::type_index(typeid(ThisType)) == _tidx) return true; \ if (std::tuple_size::value == 0) return false; \ return baseTypeContainer.find(_tidx) != baseTypeContainer.end(); \ } \ template  \ static std::set getTypeIndexes(std::tuple) { \ return std::set{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 std::enable_if::value, bool>::type instanceOf() { \ return instanceOfHelper(std::type_index(typeid(Of))); \ } #define INSTANCE_OF_IMPL(Class) \ const std::set 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  #include  #include "DemoClassHierarchy.hpp" int main() { A *a2aPtr = new A; A *a2bPtr = new B; std::shared_ptr a2cPtr(new C); C *c2dPtr = new D; std::unique_ptr a2dPtr(new D); std::cout << "a2aPtr->instanceOf(): expected=1, value=" << a2aPtr->instanceOf() << std::endl; std::cout << "a2aPtr->instanceOf(): expected=0, value=" << a2aPtr->instanceOf() << std::endl; std::cout << "a2aPtr->instanceOf(): expected=0, value=" << a2aPtr->instanceOf() << std::endl; std::cout << "a2aPtr->instanceOf(): expected=0, value=" << a2aPtr->instanceOf() << std::endl; std::cout << std::endl; std::cout << "a2bPtr->instanceOf(): expected=1, value=" << a2bPtr->instanceOf() << std::endl; std::cout << "a2bPtr->instanceOf(): expected=1, value=" << a2bPtr->instanceOf() << std::endl; std::cout << "a2bPtr->instanceOf(): expected=0, value=" << a2bPtr->instanceOf() << std::endl; std::cout << "a2bPtr->instanceOf(): expected=0, value=" << a2bPtr->instanceOf() << std::endl; std::cout << std::endl; std::cout << "a2cPtr->instanceOf(): expected=1, value=" << a2cPtr->instanceOf() << std::endl; std::cout << "a2cPtr->instanceOf(): expected=0, value=" << a2cPtr->instanceOf() << std::endl; std::cout << "a2cPtr->instanceOf(): expected=1, value=" << a2cPtr->instanceOf() << std::endl; std::cout << "a2cPtr->instanceOf(): expected=0, value=" << a2cPtr->instanceOf() << std::endl; std::cout << std::endl; std::cout << "c2dPtr->instanceOf(): expected=1, value=" << c2dPtr->instanceOf() << std::endl; std::cout << "c2dPtr->instanceOf(): expected=0, value=" << c2dPtr->instanceOf() << std::endl; std::cout << "c2dPtr->instanceOf(): expected=1, value=" << c2dPtr->instanceOf() << std::endl; std::cout << "c2dPtr->instanceOf(): expected=1, value=" << c2dPtr->instanceOf() << std::endl; std::cout << std::endl; std::cout << "a2dPtr->instanceOf(): expected=1, value=" << a2dPtr->instanceOf() << std::endl; std::cout << "a2dPtr->instanceOf(): expected=0, value=" << a2dPtr->instanceOf() << std::endl; std::cout << "a2dPtr->instanceOf(): expected=1, value=" << a2dPtr->instanceOf() << std::endl; std::cout << "a2dPtr->instanceOf(): expected=1, value=" << a2dPtr->instanceOf() << std::endl; delete a2aPtr; delete a2bPtr; delete c2dPtr; return 0; } 

Вывод:

 a2aPtr->instanceOf(): expected=1, value=1 a2aPtr->instanceOf(): expected=0, value=0 a2aPtr->instanceOf(): expected=0, value=0 a2aPtr->instanceOf(): expected=0, value=0 a2bPtr->instanceOf(): expected=1, value=1 a2bPtr->instanceOf(): expected=1, value=1 a2bPtr->instanceOf(): expected=0, value=0 a2bPtr->instanceOf(): expected=0, value=0 a2cPtr->instanceOf(): expected=1, value=1 a2cPtr->instanceOf(): expected=0, value=0 a2cPtr->instanceOf(): expected=1, value=1 a2cPtr->instanceOf(): expected=0, value=0 c2dPtr->instanceOf(): expected=1, value=1 c2dPtr->instanceOf(): expected=0, value=0 c2dPtr->instanceOf(): expected=1, value=1 c2dPtr->instanceOf(): expected=1, value=1 a2dPtr->instanceOf(): expected=1, value=1 a2dPtr->instanceOf(): expected=0, value=0 a2dPtr->instanceOf(): expected=1, value=1 a2dPtr->instanceOf(): expected=1, value=1 

Представление

Самый интересный вопрос, который сейчас возникает, заключается в том, что этот злой материал более эффективен, чем использование dynamic_cast . Поэтому я написал очень основное приложение для измерения производительности.

InstanceOfPerformance.cpp

 #include  #include  #include  #include "DemoClassHierarchy.hpp" template  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(); delete ptr; } auto end = std::chrono::high_resolution_clock::now(); return std::chrono::duration_cast(end - start); } template  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(ptr) != nullptr; delete ptr; } auto end = std::chrono::high_resolution_clock::now(); return std::chrono::duration_cast(end - start); } int main() { unsigned testCycles = 10000000; std::string unit = " us"; using DType = std::chrono::microseconds; std::cout << "InstanceOf performance(A->D) : " << instanceOfMeasurement(testCycles).count() << unit << std::endl; std::cout << "InstanceOf performance(A->C) : " << instanceOfMeasurement(testCycles).count() << unit << std::endl; std::cout << "InstanceOf performance(A->B) : " << instanceOfMeasurement(testCycles).count() << unit << std::endl; std::cout << "InstanceOf performance(A->A) : " << instanceOfMeasurement(testCycles).count() << unit << "\n" << std::endl; std::cout << "DynamicCast performance(A->D) : " << dynamicCastMeasurement(testCycles).count() << unit << std::endl; std::cout << "DynamicCast performance(A->C) : " << dynamicCastMeasurement(testCycles).count() << unit << std::endl; std::cout << "DynamicCast performance(A->B) : " << dynamicCastMeasurement(testCycles).count() << unit << std::endl; std::cout << "DynamicCast performance(A->A) : " << dynamicCastMeasurement(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 НО stream поворачивается, настраивая наш двоичный файл, используя отпимирование компилятора. Соответствующей командой компилятора является 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 и метапрограммирования шаблонов и не слишком ленивы, чтобы добавить небольшие инструкции к classам вашей иерархии classов, тогда этот подход может немного увеличить ваше приложение в отношении его производительности, если вы часто заканчиваете проверку экземпляра указателя. Но используйте его с осторожностью . Нет гарантии правильности такого подхода.

Примечание. Все демонстрации были скомпилированы с использованием clang (Apple LLVM version 9.0.0 (clang-900.0.39.2)) под MacOS Sierra на MacBook Pro в середине 2012 года.

Изменить: я также тестировал производительность на машине 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 
 #include  #include template 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); } 

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

 #include #include #include #define SIZE 20 using namespace std; class Publication { protected: char title[SIZE]; int price; public: Publication() { cout<>title; cout<>price; } virtual void show()=0; }; class Book : public Publication { int pages; public: Book() { cout<>pages; } void show() { cout<>duration; } void show() { cout<>n; Publication **p = new Publication*[n]; cout<>type; if ( type == 1 ) { p[i] = new Book(); } else if ( type == 2 ) { p[i] = new Tape(); } else { i--; cout<show(); } } return 0; } 
  • Функциональный указатель на функцию-член
  • Перегрузка метода для нулевого аргумента
  • Почему я должен избегать множественного наследования в C ++?
  • Расширяет JFrame и создает его внутри программы
  • Есть ли больше для интерфейса, чем правильные методы
  • Почему открытые поля быстрее, чем свойства?
  • Почему C ++ STL настолько сильно зависит от шаблонов? (а не на * интерфейсах *)
  • Когда следует использовать «это» в classе?
  • Почему используются неназванные пространства имен и каковы их преимущества?
  • Почему в этом коде нельзя использовать функцию защищенного члена classа?
  • Как вызвать функцию родительского classа из производной функции classа?
  • Давайте будем гением компьютера.