Я думав про те, як використовувати, typeid()
але я не знаю, як запитати, чи є цей тип підкласом іншого класу (який, до речі, абстрактний)
Відповіді:
Ти справді не повинен. Якщо вашій програмі потрібно знати, якого класу є об'єкт, це зазвичай вказує на недолік дизайну. Подивіться, чи можете ви отримати потрібну поведінку за допомогою віртуальних функцій. Крім того, допомогло б більше інформації про те, що ви намагаєтесь зробити.
Я припускаю, що у вас така ситуація:
class Base;
class A : public Base {...};
class B : public Base {...};
void foo(Base *p)
{
if(/* p is A */) /* do X */
else /* do Y */
}
Якщо це те, що у вас є, спробуйте зробити щось подібне:
class Base
{
virtual void bar() = 0;
};
class A : public Base
{
void bar() {/* do X */}
};
class B : public Base
{
void bar() {/* do Y */}
};
void foo(Base *p)
{
p->bar();
}
Редагувати: Оскільки суперечки щодо цієї відповіді все ще тривають через стільки років, я подумав, що слід додати деякі посилання. Якщо у вас є вказівник або посилання на базовий клас, і ваш код повинен знати похідний клас об’єкта, то це порушує принцип заміщення Лісковим . Дядько Боб називає це " анафемою об'єктно-орієнтованого проектування ".
class Base
{
public: virtual ~Base() {}
};
class D1: public Base {};
class D2: public Base {};
int main(int argc,char* argv[]);
{
D1 d1;
D2 d2;
Base* x = (argc > 2)?&d1:&d2;
if (dynamic_cast<D2*>(x) == nullptr)
{
std::cout << "NOT A D2" << std::endl;
}
if (dynamic_cast<D1*>(x) == nullptr)
{
std::cout << "NOT A D1" << std::endl;
}
}
dynamic_cast<>
тут? Хіба цього не static_cast<>
вистачить?
x
під час компіляції? Якщо так, тоді static_cast<>()
це спрацює. Якщо ви не можете визначити тип x
до часу виконання, тоді вам потрібноdynamic_cast<>()
Ви можете це зробити dynamic_cast
(принаймні для поліморфних типів).
Насправді, якщо задуматися - ви не можете сказати, чи конкретно це конкретний тип за допомогою dynamic_cast
- але ви можете сказати, чи це тип, чи будь-який його підклас.
template <class DstType, class SrcType>
bool IsType(const SrcType* src)
{
return dynamic_cast<const DstType*>(src) != nullptr;
}
std::is_polymorphic_v<T>
є false
.
Код нижче демонструє 3 різні способи зробити це:
#include <iostream>
#include <typeinfo>
#include <typeindex>
enum class Type {Base, A, B};
class Base {
public:
virtual ~Base() = default;
virtual Type type() const {
return Type::Base;
}
};
class A : public Base {
Type type() const override {
return Type::A;
}
};
class B : public Base {
Type type() const override {
return Type::B;
}
};
int main()
{
const char *typemsg;
A a;
B b;
Base *base = &a; // = &b; !!!!!!!!!!!!!!!!!
Base &bbb = *base;
// below you can replace base with &bbb and get the same results
// USING virtual function
// ======================
// classes need to be in your control
switch(base->type()) {
case Type::A:
typemsg = "type A";
break;
case Type::B:
typemsg = "type B";
break;
default:
typemsg = "unknown";
}
std::cout << typemsg << std::endl;
// USING typeid
// ======================
// needs RTTI. under gcc, avoid -fno-rtti
std::type_index ti(typeid(*base));
if (ti == std::type_index(typeid(A))) {
typemsg = "type A";
} else if (ti == std::type_index(typeid(B))) {
typemsg = "type B";
} else {
typemsg = "unknown";
}
std::cout << typemsg << std::endl;
// USING dynamic_cast
// ======================
// needs RTTI. under gcc, avoid -fno-rtti
if (dynamic_cast</*const*/ A*>(base)) {
typemsg = "type A";
} else if (dynamic_cast</*const*/ B*>(base)) {
typemsg = "type B";
} else {
typemsg = "unknown";
}
std::cout << typemsg << std::endl;
}
Програма вище друкує це:
type A
type A
type A
dynamic_cast
може визначити, чи містить тип цільовий тип де-небудь в ієрархії успадкування (так, це маловідома особливість, яка, якщо B
успадковує від A
та C
, може перетворити A*
безпосередньо на a C*
). typeid()
може визначити точний тип об’єкта. Однак ними обом слід користуватися надзвичайно економно. Як уже зазначалося, завжди слід уникати динамічної ідентифікації типу, оскільки це вказує на недолік конструкції. (також, якщо ви знаєте, що об'єкт точно відповідає цільовому типу, ви можете зробити пониження за допомогою a static_cast
. Boost пропонує таке, polymorphic_downcast
що зробить пониження за допомогою dynamic_cast
і assert
в режимі налагодження, а в режимі випуску він просто використовуватиме a static_cast
).
Я не згоден з тим, що вам ніколи не слід перевіряти тип об'єкта в C ++. Якщо ви можете цього уникнути, я погоджуюсь, що вам слід. Сказання, що ви НІКОЛИ не повинні робити цього за будь-яких обставин, заходить занадто далеко. Ви можете робити це на багатьох мовах, і це може значно полегшити ваше життя. Говард Пінслі, наприклад, показав нам це у своєму дописі на C #.
Я багато працюю з Qt Framework. Загалом, я моделюю те, що я роблю, як вони роблять речі (принаймні, працюючи в їх рамках). Клас QObject є базовим класом усіх об'єктів Qt. Цей клас має функції isWidgetType () та isWindowType () як швидку перевірку підкласу. То чому б не мати можливості перевірити власні похідні класи, які порівнянні за своєю природою? Ось випуск QObject з деяких з цих інших постів:
class MyQObject : public QObject
{
public:
MyQObject( QObject *parent = 0 ) : QObject( parent ){}
~MyQObject(){}
static bool isThisType( const QObject *qObj )
{ return ( dynamic_cast<const MyQObject*>(qObj) != NULL ); }
};
А потім, коли ви передаєте вказівник на QObject, ви можете перевірити, чи вказує він на ваш похідний клас, викликаючи функцію статичного члена:
if( MyQObject::isThisType( qObjPtr ) ) qDebug() << "This is a MyQObject!";
Я не знаю, чи правильно я розумію вашу проблему, тому дозвольте повторити це своїми словами ...
Проблема: Дано класи B
та D
визначено, чи D
є підкласом B
(або навпаки?)
Рішення: Використовуйте магію шаблону! Добре, серйозно вам потрібно поглянути на LOKI, чудову бібліотеку метапрограмування шаблонів, вироблену легендарним автором C ++ Андрієм Александреску.
Більш конкретно, завантажте LOKI та включіть TypeManip.h
із нього заголовок у свій вихідний код, а потім використовуйте SuperSubclass
шаблон класу наступним чином:
if(SuperSubClass<B,D>::value)
{
...
}
Згідно з документацією, SuperSubClass<B,D>::value
істина буде, якщо B
є загальнодоступною базою D
, або якщо B
і D
є псевдонімами одного типу.
тобто або D
є підкласом B
або D
є таким самим як B
.
Сподіваюся, це допоможе.
редагувати:
Зверніть увагу, оцінка того, що SuperSubClass<B,D>::value
відбувається під час компіляції, на відміну від деяких методів, які використовують dynamic_cast
, отже, не застосовується штраф за використання цієї системи під час виконання.
#include <stdio.h>
#include <iostream.h>
class Base
{
public: virtual ~Base() {}
template<typename T>
bool isA() {
return (dynamic_cast<T*>(this) != NULL);
}
};
class D1: public Base {};
class D2: public Base {};
class D22: public D2 {};
int main(int argc,char* argv[]);
{
D1* d1 = new D1();
D2* d2 = new D2();
D22* d22 = new D22();
Base* x = d22;
if( x->isA<D22>() )
{
std::cout << "IS A D22" << std::endl;
}
if( x->isA<D2>() )
{
std::cout << "IS A D2" << std::endl;
}
if( x->isA<D1>() )
{
std::cout << "IS A D1" << std::endl;
}
if(x->isA<Base>() )
{
std::cout << "IS A Base" << std::endl;
}
}
Результат:
IS A D22
IS A D2
IS A Base
Я думав, як використовувати
typeid()
...
Ну, да, це може бути зроблено шляхом порівняння: typeid().name()
. Якщо взяти вже описану ситуацію, де:
class Base;
class A : public Base {...};
class B : public Base {...};
void foo(Base *p)
{
if(/* p is A */) /* do X */
else /* do Y */
}
Можливою реалізацією foo(Base *p)
буде:
#include <typeinfo>
void foo(Base *p)
{
if(typeid(*p) == typeid(A))
{
// the pointer is pointing to the derived class A
}
else if (typeid(*p).name() == typeid(B).name())
{
// the pointer is pointing to the derived class B
}
}
Ви можете зробити це лише під час компіляції за допомогою шаблонів, якщо ви не використовуєте RTTI.
Це дозволяє використовувати функцію typeid, яка видасть покажчик на структуру type_info, яка містить інформацію про тип.
Прочитайте про це у Вікіпедії
У c # ви можете просто сказати:
if (myObj is Car) {
}
Ви можете зробити це за допомогою шаблонів (або SFINAE (Помилка заміщення не є помилкою)). Приклад:
#include <iostream>
class base
{
public:
virtual ~base() = default;
};
template <
class type,
class = decltype(
static_cast<base*>(static_cast<type*>(0))
)
>
bool check(type)
{
return true;
}
bool check(...)
{
return false;
}
class child : public base
{
public:
virtual ~child() = default;
};
class grandchild : public child {};
int main()
{
std::cout << std::boolalpha;
std::cout << "base: " << check(base()) << '\n';
std::cout << "child: " << check(child()) << '\n';
std::cout << "grandchild: " << check(grandchild()) << '\n';
std::cout << "int: " << check(int()) << '\n';
std::cout << std::flush;
}
Вихід:
base: true
child: true
grandchild: true
int: false
std::is_base_of
він не працюватиме за бажанням. : 3