Estaba pensando en el uso, typeid()
pero no sé cómo preguntar si ese tipo es una subclase de otra clase (que, por cierto, es abstracta)
Respuestas:
Realmente no deberías. Si su programa necesita saber qué clase es un objeto, eso generalmente indica un defecto de diseño. Vea si puede obtener el comportamiento que desea usando funciones virtuales. Además, sería útil contar con más información sobre lo que está intentando hacer.
Supongo que tienes una situación como esta:
class Base;
class A : public Base {...};
class B : public Base {...};
void foo(Base *p)
{
if(/* p is A */) /* do X */
else /* do Y */
}
Si esto es lo que tiene, intente hacer algo como esto:
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();
}
Editar: Dado que el debate sobre esta respuesta aún continúa después de tantos años, pensé que debería incluir algunas referencias. Si tiene un puntero o referencia a una clase base y su código necesita conocer la clase derivada del objeto, entonces viola el principio de sustitución de Liskov . El tío Bob llama a esto un " anatema para el diseño orientado a objetos ".
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<>
aquí? ¿No static_cast<>
sería suficiente?
x
en tiempo de compilación? Si es así static_cast<>()
, funcionaría. Si no se puede saber el tipo de x
hasta el tiempo de ejecución, entonces usted necesitadynamic_cast<>()
Puedes hacerlo con dynamic_cast
(al menos para tipos polimórficos).
En realidad, pensándolo bien, no puede decir si es ESPECÍFICAMENTE un tipo en particular con, dynamic_cast
pero puede decir si es ese tipo o alguna subclase del mismo.
template <class DstType, class SrcType>
bool IsType(const SrcType* src)
{
return dynamic_cast<const DstType*>(src) != nullptr;
}
std::is_polymorphic_v<T>
sea false
.
El siguiente código muestra 3 formas diferentes de hacerlo:
#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;
}
El programa de arriba imprime esto:
type A
type A
type A
dynamic_cast
puede determinar si el tipo contiene el tipo de destino en algún lugar de la jerarquía de herencia (sí, es una característica poco conocida que si B
hereda de A
y C
, puede convertir una A*
directamente en a C*
). typeid()
puede determinar el tipo exacto del objeto. Sin embargo, ambos deben usarse con extrema moderación. Como ya se mencionó, siempre debe evitar la identificación dinámica de tipos, ya que indica un defecto de diseño. (Además, si sabe que el objeto es seguro del tipo de objetivo, puede hacer un abatimiento con un static_cast
. Boost ofrece un polymorphic_downcast
que hará un abatimiento con dynamic_cast
y assert
en modo de depuración, y en el modo de lanzamiento solo usará a static_cast
).
No estoy de acuerdo en que nunca debería querer verificar el tipo de un objeto en C ++. Si puede evitarlo, estoy de acuerdo en que debería hacerlo. Sin embargo, decir que NUNCA debes hacer esto bajo ninguna circunstancia es ir demasiado lejos. Puede hacer esto en muchos idiomas y puede hacer su vida mucho más fácil. Howard Pinsley, por ejemplo, nos mostró cómo en su publicación sobre C #.
Trabajo mucho con Qt Framework. En general, modelo lo que hago según la forma en que hacen las cosas (al menos cuando trabajan en su marco). La clase QObject es la clase base de todos los objetos Qt. Esa clase tiene las funciones isWidgetType () e isWindowType () como una verificación rápida de subclase. Entonces, ¿por qué no poder verificar sus propias clases derivadas, que son comparables en su naturaleza? Aquí hay un derivado de QObject de algunas de estas otras publicaciones:
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 ); }
};
Y luego, cuando pasa un puntero a un QObject, puede verificar si apunta a su clase derivada llamando a la función miembro estática:
if( MyQObject::isThisType( qObjPtr ) ) qDebug() << "This is a MyQObject!";
No sé si entiendo correctamente su problema, así que permítame repetirlo con mis propias palabras ...
Problema: Dadas las clases B
y D
, determine si D
es una subclase de B
(¿o viceversa?)
Solución: ¡Usa un poco de magia de plantilla! Bien, en serio, debes echar un vistazo a LOKI, una excelente biblioteca de metaprogramación de plantillas producida por el legendario autor de C ++ Andrei Alexandrescu.
Más específicamente, descargue LOKI e incluya el encabezado TypeManip.h
en su código fuente y luego use la SuperSubclass
plantilla de clase de la siguiente manera:
if(SuperSubClass<B,D>::value)
{
...
}
De acuerdo con la documentación, SuperSubClass<B,D>::value
será verdadero si B
es una base pública de D
, o si B
y D
son alias del mismo tipo.
es decir, D
es una subclase de B
o D
es lo mismo que B
.
Espero que esto ayude.
editar:
Tenga en cuenta que la evaluación de SuperSubClass<B,D>::value
ocurre en tiempo de compilación a diferencia de algunos métodos que lo usan dynamic_cast
, por lo tanto, no hay penalización por usar este sistema en tiempo de ejecución.
#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;
}
}
Resultado:
IS A D22
IS A D2
IS A Base
Estaba pensando en el uso de
typeid()
...
Bueno, sí, se podría hacer mediante la comparación: typeid().name()
. Si tomamos la situación ya descrita, donde:
class Base;
class A : public Base {...};
class B : public Base {...};
void foo(Base *p)
{
if(/* p is A */) /* do X */
else /* do Y */
}
Una posible implementación de foo(Base *p)
sería:
#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
}
}
Solo puede hacerlo en tiempo de compilación usando plantillas, a menos que use RTTI.
Te permite usar la función typeid que producirá un puntero a una estructura type_info que contiene información sobre el tipo.
Leer sobre ello en Wikipedia
En c # puedes simplemente decir:
if (myObj is Car) {
}
Puede hacerlo con plantillas (o SFINAE (La falla de sustitución no es un error)). Ejemplo:
#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;
}
Salida:
base: true
child: true
grandchild: true
int: false
std::is_base_of
no funcionará como se desea. : 3