C++ equivalent of instanceof
实现与
尝试使用:
1 2 3 4 | if(NewType* v = dynamic_cast<NewType*>(old)) { // old was safely casted to NewType v->doSomething(); } |
这要求您的编译器启用rtti支持。
编辑:
我对这个答案有很好的评论!
每次需要使用dynamic_cast(或instanceof)时,最好问问自己是否必要。通常这是不良设计的标志。
典型的变通办法是将要检查的类的特殊行为放入基类的虚函数中,或者引入诸如访问者之类的东西,您可以在不更改接口的情况下为子类引入特定行为(除了添加的访问者接受接口)课程)。
如前所述,dynamic_cast不是免费提供的。一个可以处理大多数(但不是所有情况)的简单且性能一致的hack基本上是添加一个枚举,表示您的类可以拥有的所有可能的类型,并检查您是否找到了正确的类型。
1 2 3 4 | if(old->getType() == BOX) { Box* box = static_cast<Box*>(old); // Do something box specific } |
这不是一个好的设计,但它可能是一种解决方法,其成本或多或少只是一个虚拟函数调用。无论是否启用了RTTI,它也可以工作。
请注意,这种方法不支持多级继承,因此,如果您不小心,可能会以如下代码结尾:
1 2 3 4 5 6 | // 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 } |
根据您要执行的操作,可以执行以下操作:
1 2 3 4 | template<typename Base, typename T> inline bool instanceof(const T*) { return std::is_base_of<Base, T>::value; } |
采用:
1 | if (instanceof<BaseClass>(ptr)) { ... } |
但是,这仅对编译器已知的类型起作用。
编辑:
此代码应适用于多态指针:
1 2 3 4 | 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标准,您现在可以实现
1 2 3 4 5 | if (dynamic_cast<B*>(aPtr) != nullptr) { // aPtr is instance of B } else { // aPtr is NOT instance of B } |
但是您仍然依赖
InstanceOfMacros.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | #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 *
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | #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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | #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(): expected=0, value=" << a2aPtr->instanceOf() << 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(): expected=1, value=" << a2bPtr->instanceOf() << 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(): expected=0, value=" << a2cPtr->instanceOf() << 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(): expected=0, value=" << c2dPtr->instanceOf() << 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(): expected=0, value=" << a2dPtr->instanceOf() << 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; } |
输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | a2aPtr->instanceOf<A>(): expected=1, value=1 a2aPtr->instanceOf(): 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(): 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(): 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(): 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(): expected=0, value=0 a2dPtr->instanceOf<C>(): expected=1, value=1 a2dPtr->instanceOf<D>(): expected=1, value=1 |
性能
现在出现的最有趣的问题是,这种邪恶的东西是否比
InstanceOfPerformance.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | #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 <<" " << 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 <<" " << std::endl; return 0; } |
结果有所不同,并且基本上基于编译器的优化程度。使用
1 2 3 4 5 6 7 8 9 | 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 |
嗯,这个结果非常令人震惊,因为时间表明,新方法与
1 2 3 4 5 6 7 8 9 | 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和模板元编程,并且不懒于向类层次结构的类中添加一些小指令,那么这种方法可以使您的应用程序有所提升关于它的性能,如果您经常要检查指针的实例。但是请谨慎使用。不保证这种方法的正确性。
注意:所有演示都是使用MacBook Pro Mid 2012上的macOS Sierra下的
编辑:
我还使用
输出(没有编译器优化):
1 2 3 4 5 6 7 8 9 | 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 |
输出(使用编译器优化):
1 2 3 4 5 6 7 8 9 | 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 |
已知
但是,如果
1 2 3 4 | template<typename T, typename K> inline bool isType(const K &k) { return typeid(T).hash_code() == typeid(k).hash_code(); } |
这是如何调用上面的函数的示例:
1 2 3 4 5 | DerivedA k; Base *p = &k; cout << boolalpha << isType<DerivedA>(*p) << endl; // true cout << boolalpha << isType<DerivedB>(*p) << endl; // false |
您将指定模板类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #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); } |
这对我来说非常适合使用Code :: Blocks IDE和GCC编译器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | #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; } |