Help me find a quick method for checking the type of an object. There was a simple public inheritance and I used dynamic_cast . It was all good.

class BaseVirtual{}; class A:public BaseVirtual {}; class B:public BaseVirtual {}; bool isA(BaseVirtual * x){ return dynamic_cast<A*>(x);} 

Then a class with protected inheritance appeared, but dynamic_cast refuses to work, it always returns nullptr .

 class C:protected BaseVirtual {}; bool isC(BaseVirtual * x){ return dynamic_cast<C*>(x);} // всегда FALSE 

Once used virtual functions, but refused for speed. Switched to dynamic_cast .

 //--- результат опроса --- 

Suppose I returned to the implementation using virtual functions. Imagine that all classes are inherited in protected mode. How many methods you need to determine if the classes for example fifty Therefore, the question remains the same: how to get rid of virtual methods?

 // g++ -std=c++11 protclas.cpp -o protclas # include <iostream> class A{ public: virtual bool isB(){return false;} virtual bool isC(){return false;} virtual ~A(){} }; class B:public A{ public: virtual ~B(){} virtual bool isB(){return true;} }; class C:protected A{ public: virtual ~C(){} virtual bool isC(){return true;} }; int main(){ A * x=reinterpret_cast<A*>( new C); A * y=reinterpret_cast<A*>( new B); std::cout<<"x=C:(x->isC())="<<(x->isC())<<std::endl; std::cout<<"y=B:(y->isC())="<<(y->isC())<<std::endl; delete y; delete x;} 
  • function isC class method? - Andrej Levkovitch
  • isC is an external question - AlexGlebe
  • I strongly doubt that the virtuality has dropped by speed. The dynamic_cast will most likely be somewhere the same in speed. Benchmarks done or "one grandmother said?" - KoVadim
  • one
    Make isC a friend of class C. In general, something strange about architecture. If no one except the heirs should know that C is the heir of the base class, then why would you need such a check? - Chorkov
  • one
    “I used to use virtual functions, but refused for speed. Switched to dynamic_cast. " And what, did the profiler show that the solution with dynamic_cast works faster? - ixSci

5 answers 5

Answering the second part of the corrected question: No need to try to write isB / isC either in the form of virtual functions or in the form of external functions. Ideally, the number of virtual methods of the base class should correspond to the number of points of use of this interface. So instead

 case ParaLispObject::TypeType::TypeBIF : if ( dynamic_cast < PLOBIF * > ( & x ) ) goto OK ; symboltypename = plosbif ; break 

We will write:

 class BaseVirtual { public: virtual bool isCompatible( ParaLispObject::TypeType ) { return false; } virtual const char* symbolTypeName() { return ""; } }; // ... switch( foo ) { // ... case ParaLispObject::TypeType::TypeBIF : if ( x.isCompatible(foo) ) goto OK ; symboltypename = x.symbolTypeName() ; break ; 

Probably, this will allow partially or completely get rid of the switch.

  • Good idea about type checking, wrote virtual bool isType ( Type t ) { return t == Type::TC ; } virtual bool isType ( Type t ) { return t == Type::TC ; } . Everything plows. - AlexGlebe 4:16 pm

In general, it is very strange that you have:

 bool isC(BaseVirtual * x){ return dynamic_cast<C*>(x);} // всегда FALSE 

Although when trying to compile this code (which of course is not complete and I had to manually add a constructor and a zeroed virtual function), the compiler quite clearly gives an error:

 1.cpp:37:22: error: cannot cast 'C' to its protected base class 'BaseVirtual' std::cout << isA(&c) << std::endl; 
  • New object C when sent to the outside world is always sent by a pointer to reinterpret_cast<BaseVirtual*> - AlexGlebe
  • one
    @AlexGlebe Ie because you can not automatically bring the type from C to BaseVirtual you use reinterpret_cast ? And then you want it to lead back to C using dynamic_cast ? Do you yourself think this is strange? - Andrej Levkovitch
  • @AlexGlebe I'm not an expert, but, IMHO, due to the fact that your ancestor is declared as private automatic casting is impossible, since the pointer to the base class simply does not see the valid cast - Andrej Levkovitch
  • I do not see anything strange, protected inheritance protects public elements, they become protected . It is necessary for practical purposes. The code from reinterpret_cast does not become inoperative. (If you do everything right.) - AlexGlebe
  • @AlexGlebe reinterpret_cast simply interprets the data - it will translate the pointer into a pointer to anything - you can use it instead of dynamic_cast - and you will always have true even if it is not the same object. - Andrej Levkovitch

Since class C:protected BaseVirtual {}; class С and its heirs use for their implementation all the open and protected members of the BaseVirtual class.

But objects of type С can be of type BaseVirtual as much as any arbitrary type ( т.е. не могут являются ), and it is impossible to bring BaseVirtual* в C* if this is not done in the functions of these classes ...

So that

  bool isC(BaseVirtual * x){ return dynamic_cast<C*>(x);} 

obviously, it will always return false

    Without using virtual functions, you can determine the actual object in memory if the object has at least one virtual function. The typeid function returns a type_info table. You can compare these tables. If the class has no virtual functions, then information about this type.

     // g++ -std=c++11 protclas2.cpp -o protclas2 # include <iostream> # include <typeinfo> class SA{}; class SPuB:public SA{}; class SProC:protected SA{}; class SPriD:private SA{}; class A{ public: virtual ~A(){} }; class B:public A{ public: virtual ~B(){} }; class C:protected A{ public: virtual ~C(){} }; class D:private A{ public: virtual ~D(){} }; int main(){ A * z=reinterpret_cast<A*>( new A); A * x=reinterpret_cast<A*>( new C); A * y=reinterpret_cast<A*>( new B); A * v=reinterpret_cast<A*>( new D); SA * sa=reinterpret_cast<SA*>( new SA); SA * spub=reinterpret_cast<SA*>( new SPuB); SA * sproc=reinterpret_cast<SA*>( new SProC); SA * sprid=reinterpret_cast<SA*>( new SPriD); if(typeid(*z)==typeid(A))std::cout<<"z==A"<<std::endl; if(typeid(*z)==typeid(B))std::cout<<"z==B"<<std::endl; if(typeid(*z)==typeid(C))std::cout<<"z==C"<<std::endl; if(typeid(*z)==typeid(D))std::cout<<"z==D"<<std::endl; if(typeid(*x)==typeid(A))std::cout<<"x==A"<<std::endl; if(typeid(*x)==typeid(B))std::cout<<"x==B"<<std::endl; if(typeid(*x)==typeid(C))std::cout<<"x==C"<<std::endl; if(typeid(*x)==typeid(D))std::cout<<"x==D"<<std::endl; if(typeid(*y)==typeid(A))std::cout<<"y==A"<<std::endl; if(typeid(*y)==typeid(B))std::cout<<"y==B"<<std::endl; if(typeid(*y)==typeid(C))std::cout<<"y==C"<<std::endl; if(typeid(*y)==typeid(D))std::cout<<"y==D"<<std::endl; if(typeid(*v)==typeid(A))std::cout<<"v==A"<<std::endl; if(typeid(*v)==typeid(B))std::cout<<"v==B"<<std::endl; if(typeid(*v)==typeid(C))std::cout<<"v==C"<<std::endl; if(typeid(*v)==typeid(D))std::cout<<"v==D"<<std::endl; if(typeid(*sa)==typeid(SA))std::cout<<"sa==SA"<<std::endl; if(typeid(*sa)==typeid(SPuB))std::cout<<"sa==SPuB"<<std::endl; if(typeid(*sa)==typeid(SProC))std::cout<<"sa==SProC"<<std::endl; if(typeid(*sa)==typeid(SPriD))std::cout<<"sa==SPriD"<<std::endl; if(typeid(*spub)==typeid(SA))std::cout<<"spub==SA"<<std::endl; if(typeid(*spub)==typeid(SPuB))std::cout<<"spub==SPuB"<<std::endl; if(typeid(*spub)==typeid(SProC))std::cout<<"spub==SProC"<<std::endl; if(typeid(*spub)==typeid(SPriD))std::cout<<"spub==SPriD"<<std::endl; if(typeid(*sproc)==typeid(SA))std::cout<<"sproc==SA"<<std::endl; if(typeid(*sproc)==typeid(SPuB))std::cout<<"sproc==SPuB"<<std::endl; if(typeid(*sproc)==typeid(SProC))std::cout<<"sproc==SProC"<<std::endl; if(typeid(*sproc)==typeid(SPriD))std::cout<<"sproc==SPriD"<<std::endl; if(typeid(*sprid)==typeid(SA))std::cout<<"sprid==SA"<<std::endl; if(typeid(*sprid)==typeid(SPuB))std::cout<<"sprid==SPuB"<<std::endl; if(typeid(*sprid)==typeid(SProC))std::cout<<"sprid==SProC"<<std::endl; if(typeid(*sprid)==typeid(SPriD))std::cout<<"sprid==SPriD"<<std::endl; delete sprid; delete sproc; delete spub; delete sa; delete v; delete y; delete x; delete z;} 

    This example returns the result that all objects with virtual functions are recognized from any type of pointer. Simple types are not recognized because they do not have type tables.

     z==A x==C y==B v==D sa==SA spub==SA sproc==SA sprid==SA 

    typeid still lost to virtual methods, when comparing the two types, strcmp is called . I'm shocked.

     /usr/include/c++/7/typeinfo: // Even with the new abi, on systems that support dlopen // we can run into cases where type_info names aren't merged, // so we still need to do string comparison. bool operator==(const type_info& __arg) const _GLIBCXX_NOEXCEPT { return ((__name == __arg.__name) || (__name[0] != '*' && __builtin_strcmp (__name, __arg.__name) == 0)); } 

      The English version found the most successful: without virtual calls, reading the strings of the class name. Only one type code variable is needed. https://stackoverflow.com/questions/28172306/efficient-run-time-type-checking-in-c

       // g++ -std=c++11 gettype.cpp -o gettype # include <iostream> enum Type : uint8_t { TBase , TNumber } ; class Base { protected : Type t { TBase } ; public : Base(){} Base(Type x):t{x}{} Type GetType()const{return t;} } ; class Number : protected Base { public: Number():Base(TNumber){} } ; int main(){ Number n; Base * np = reinterpret_cast < Base * > ( & n ) ; std::cout<<"*np = "<<((np->GetType() == TNumber)?"Number":"NotNumber")<<std::endl; }