Я думав про те, як використовувати, 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