我正在考虑使用typeid()
,但我不知道如何询问该类型是否是另一个类的子类(顺便说一下,它是抽象的)
答案 0 :(得分:91)
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;
}
}
答案 1 :(得分:40)
你真的不应该。如果您的程序需要知道对象是什么类,那通常表明存在设计缺陷。看看您是否可以使用虚拟功能获得所需的行为。此外,有关您尝试做什么的更多信息会有所帮助。
我假设你有这样的情况:
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();
}
编辑:由于关于这个答案的辩论在这么多年后仍然存在,我想我应该引用一些参考资料。如果你有一个指向基类的指针或引用,并且你的代码需要知道对象的派生类,那么它就违反了Liskov substitution principle。 Uncle Bob将其称为“anathema to Object Oriented Design”。
答案 2 :(得分:21)
您可以使用dynamic_cast
(至少对于多态类型)。
实际上,在第二个想法 - 你无法判断它是否特别是dynamic_cast
的特定类型 - 但你可以判断它是该类型还是其任何子类。
template <class DstType, class SrcType>
bool IsType(const SrcType* src)
{
return dynamic_cast<const DstType*>(src) != nullptr;
}
答案 3 :(得分:6)
dynamic_cast
可以确定类型是否在继承层次结构中的任何位置包含目标类型(是的,如果B
继承自A
并且C
,则这是一个鲜为人知的功能},它可以将A*
直接转换为C*
)。 typeid()
可以确定对象的确切类型。但是,这两者都应该非常谨慎地使用。如前所述,您应始终避免动态类型识别,因为它表明存在设计缺陷。 (另外,如果您确定该对象确定了目标类型,则可以使用static_cast
进行向下转换.Boost会提供polymorphic_downcast
,使用dynamic_cast
进行向下转发,{ {1}}在调试模式下,在发布模式下,它只使用assert
)。
答案 4 :(得分:4)
我不知道我是否理解你的问题,所以让我用自己的话来重述......
问题:给定类B
和D
,确定D
是B
的子类(反之亦然?)
解决方案:使用一些模板魔法!好的,你需要看看LOKI,这是一个出色的模板元编程库,由传说中的C ++作者Andrei Alexandrescu制作。
更具体地说,在源代码中下载LOKI并在其中添加标题TypeManip.h
,然后使用SuperSubclass
类模板,如下所示:
if(SuperSubClass<B,D>::value)
{
...
}
根据文档,如果SuperSubClass<B,D>::value
是B
的公共基础,或者D
和B
是同一个别名,则D
将为真类型。
即。 D
是[{1}}的子类,或B
与D
相同。
我希望这会有所帮助。
修改强>
请注意B
的评估发生在编译时,与使用SuperSubClass<B,D>::value
的某些方法不同,因此在运行时使用此系统不会受到任何惩罚。
答案 5 :(得分:2)
#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
答案 6 :(得分:1)
在c#中你可以简单地说:
if (myObj is Car) {
}
答案 7 :(得分:1)
答案 8 :(得分:1)
您可以使用模板(或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
答案 9 :(得分:1)
我在这里看到了一些不错的答案,也看到了一些愚蠢的回应。
“尝试查询对象的类型是一个设计缺陷”。这意味着 Java instanceof 和 C# is 关键字是设计缺陷。这些是不评价多态性的人的反应。如果您有一个接口,则该接口由另一个实现更多功能的接口派生而来。如果您需要这些额外的功能,您必须首先检查您是否有这样的界面。甚至微软的 COM API 也使用了这种设计。
那么关于如何推断一个对象是否是一个类的实例,已经有很多很好的答案了
is_base_of 与多态无关。
并且让每个虚函数定义自己的类型方法是不必要的,因为它是多余的。每个虚拟类已经有一个指向它的虚拟表的指针。
class Base
{
void *p_virtual_table = BASE_VIRTUAL_TABLE;
}
class Derived : Base
{
void *p_virtual_table = DERIVED_VIRTUAL_TABLE;
}
void *BASE_VIRTUAL_TABLE[n];
void *DERIVED_VIRTUAL_TABLE[n];
这里的重点是虚拟表的地址是固定的,一个简单的比较将决定一个虚拟对象是否是一个虚拟类的实例。
由于 cpp 没有为我们提供访问虚拟表的标准方式,因此很难手动进行这些比较。但是cpp抽象机在推导出虚拟对象的确切实例时绝对没有问题。
答案 10 :(得分:0)
我正在考虑使用
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
}
}
答案 11 :(得分:0)
下面的代码演示了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