问题
I'm working with a class library where all classes are, directly or indirectly, derived from a base class Base
and have a name. The library provides a facility to search for objects by a name, which will return a Base*
.
Is there any way to find the type of the returned object without checking all possibilities using dynamic_cast
s as I did in the following example? I'd like to avoid that, if at all possible, since the derived classes have template parameters, which makes for quite a few possibilities.
It would also be fine if I were at least able to find out the class type (T1
or T2
, in the example below) without knowing the template type, ie. do something like a dynamic_cast<T1<i_dont_care>*>
.
#include <iostream>
using namespace std;
class Base {
public:
virtual ~Base() {}
};
template <typename T> class T1 : public Base {};
template <typename T> class T2 : public Base {};
Base *find_by_name() {
return new T2<int>();
}
int main() {
Base *b = find_by_name();
if (T1<int> *p = dynamic_cast<T1<int>*>(b)) {
cout << "T1<int>" << endl;
// do something meaningful with p
} else if (T1<double> *p = dynamic_cast<T1<double>*>(b))
cout << "T1<double>" << endl;
else if (T2<int> *p = dynamic_cast<T2<int>*>(b))
cout << "T2<int>" << endl;
else if (T2<double> *p = dynamic_cast<T2<double>*>(b))
cout << "T2<double>" << endl;
else
cout << "unknown" << endl;
delete b;
return 0;
}
Note that the above example is simplified, ie. in each if
I'd do something meaningful with p
.
I do realize that this is bad design from the very start, however I'm stuck with this library and there's also no way for me to change its implementation.
回答1:
There is a typeid operator, which returns an instance of std::type_info, with which you can get the name of the type.
Not sure if that will help you though. First, the returned name is not guaranteed to be the same across implementations. Second - what would you do once you have the name? You'd compare it with your pre-defined names probably, but that is probably slower than a bunch of dynamic_cast
's.
Without type support built into your Base
class or a new intermediate layer of hierarchy, dynamic_cast
is your best choice. In reality it will be very fast (usually just a single compare instruction).
By intermediate layer I mean:
class Base {
public:
virtual ~Base() {}
};
class T1Base : public Base {};
class T2Base : public Base {};
template <typename T> class T1 : public T1Base {};
template <typename T> class T2 : public T2Base {};
int main() {
Base *b = find_by_name();
if (dynamic_cast<T1Base*>(b))
cout << "T1" << endl;
else if (dynamic_cast<T2Base*>(b))
cout << "T2" << endl;
else
cout << "unknown" << endl;
delete b;
return 0;
}
回答2:
There's something like typeid
http://en.cppreference.com/w/cpp/language/typeid, which applied to polymorphic expression will evaluate in a runtime to its type representation.
Following wiki example: https://en.wikipedia.org/wiki/Run-time_type_information#dynamic_cast
#include <iostream>
#include <typeinfo> // for 'typeid'
class Person {
public:
virtual ~Person() {}
};
class Employee : public Person {
};
int main()
{
Person person;
Employee employee;
Person* ptr = &employee;
Person& ref = employee;
// The string returned by typeid::name is implementation-defined
// Person (statically known at compile-time)
std::cout << typeid(person).name() << std::endl;
// Employee (statically known at compile-time)
std::cout << typeid(employee).name() << std::endl;
// Person* (statically known at compile-time)
std::cout << typeid(ptr).name() << std::endl;
/* Employee (looked up dynamically at run-time
* because it is the dereference of a
* pointer to a polymorphic class) */
std::cout << typeid(*ptr).name() << std::endl;
// Employee (references can also be polymorphic)
std::cout << typeid(ref).name() << std::endl;
}
来源:https://stackoverflow.com/questions/39138770/get-objects-type-from-pointer-to-base-class-at-runtime