在C ++中查找对象的类型


147

我有一个类A和另一个继承自它的类B,我正在重写一个接受类型为A的对象作为参数的函数,因此我必须接受一个A。但是,我后来调用了只有B具有的函数,因此,如果传递的对象不是B型,我想返回false而不继续。

找出传递给我的函数的对象是哪种类型的最佳方法是什么?

Answers:


162

dynamic_cast应该可以解决问题

TYPE& dynamic_cast<TYPE&> (object);
TYPE* dynamic_cast<TYPE*> (object);

dynamic_cast关键字从一个指针或引用类型转换基准到另一个,执行运行时检查以确保铸造的有效性。

如果您尝试将指针转换为非实际对象类型的指针,则转换结果将为NULL。如果您尝试强制转换对不是实际对象类型的类型的引用,则强制转换将引发bad_cast异常。

确保在基类中至少有一个虚函数使dynamic_cast工作。

维基百科主题运行时类型信息

RTTI仅适用于多态类,这意味着它们具有至少一个虚拟方法。实际上,这不是一个限制,因为基类必须具有虚拟析构函数,以允许如果将派生类的对象从基体指针中删除,则它们可以执行适当的清除操作。


1
您的意思是在Base类中必须有一个虚函数才能使dynamic_cast工作。在我看来,这很重要,我只是猜测。
GiCo 2015年

3
OK找到了:运行时类型信息(RTTI)仅适用于多态类,这意味着它们至少具有一个虚拟方法。dynamic_cast和typeid需要RTTI。
GiCo 2015年

dynamic_cast如果它不可转换,不会抛出吗?有没有办法做到而又不会产生抛出的方法?
jww

A* aptr = dynamic_cast<A*>(ptr);//是否应该是这样
Mehdi Karamosly

这对已转换为POD的POD有用uint8_t*吗?也就是说,我可以检查uint32_t* x = dynamic_cast<uint32_t*>(p),在那里puint8_t*?(我正在尝试查找违反礼节的测试)。
jww

157

动态类型转换最适合描述问题,但是我只想补充一下,您可以使用以下方法找到类类型:

#include <typeinfo>

...
string s = typeid(YourClass).name()

4
如果您真的不知道您的对象是什么,那就很好。接受的答案假设您愿意。
Unludo 2012年

4
@xus是的。它是std标头的一部分
Amey Jah 2013年

8
不知道如何。类型ID名称不是必需的,并且是实现定义的。
Shoe 2014年

3
这里最有趣的是:同一类的实例的名称不必相等。但是,对于同一类的实例,typeid本身必须比较相等,请参见stackoverflow.com/questions/1986418/typeid-versus-typeof-in-c
FourtyTwo,2016年

1
注意gcc返回带有名称的名称,例如11MyClass。要取消破坏,可以使用中的ABI扩展库cxxabi.h。这给了您abi::__cxa_demangle真实的名字
David G

27

这称为RTTI,但是您几乎肯定要在这里重新考虑设计,因为找到类型并基于它执行特殊操作会使您的代码更脆弱。


4
真正。不幸的是,我正在做一个现有项目,所以我真的不能去更改设计或A类中的任何东西
。– lemnisca

11

为了完整起见,我将构建Robocide的构建,并指出typeid无需使用name()即可单独使用:

#include <typeinfo>
#include <iostream>

using namespace std;

class A {
public:
    virtual ~A() = default; // We're not polymorphic unless we
                            // have a virtual function.
};
class B : public A { } ;
class C : public A { } ;

int
main(int argc, char* argv[])
{
    B b;
    A& a = b;

    cout << "a is B: " << boolalpha << (typeid(a) == typeid(B)) << endl;
    cout << "a is C: " << boolalpha << (typeid(a) == typeid(C)) << endl;
    cout << "b is B: " << boolalpha << (typeid(b) == typeid(B)) << endl;
    cout << "b is A: " << boolalpha << (typeid(b) == typeid(A)) << endl;
    cout << "b is C: " << boolalpha << (typeid(b) == typeid(C)) << endl;
}

输出:

a is B: true
a is C: false
b is B: true
b is A: false
b is C: false

9

可能在您的对象中嵌入一个ID“标签”,并使用它来区分A类对象和B类对象。

但是,这表明了设计中的缺陷。理想情况下,B中没有A的那些方法应该是A的一部分,但应留空,然后B覆盖它们。这消除了特定于类的代码,更符合OOP的精神。



4

因为您的课程不是多态的。尝试:

struct BaseClas { int base; virtual ~BaseClas(){} };
class Derived1 : public BaseClas { int derived1; };

现在BaseClas是多态的。我将类更改为struct,因为默认情况下结构的成员是公共的。


3

您的描述有些混乱。

一般来说,尽管某些C ++实现具有相应的机制,但您不应该询问类型。相反,您应该在指向A的指针上进行dynamic_cast。这将在运行时检查指向A的指针的实际内容。如果您有B,则将获得指向B的指针。否则,将获得异常或null。


1
应该注意的是,只有在执行引用强制转换失败时,您才会获得异常。即dynamic_cast <T&>(t)。指针转换失败返回NULL。即dynamic_cast <T *>(t)
AlfaZulu

是的,我应该更好地阐明这一点。谢谢。我希望在C类型中有一个用引用而不是按值描述的单词。
乌里(Uri)

3

正如其他人指出的,您可以使用dynamic_cast。但是通常使用dynamic_cast来发现您正在处理的派生类的类型表明设计不好。如果要覆盖以A的指针作为参数的函数,则该函数应该能够使用A类本身的方法/数据,并且不应该依赖于B类的数据。确保要编写的方法仅适用于类B,然后应在类B中编写一个新方法。


1

使用重载函数。不需要dynamic_cast甚至RTTI支持:

class A {};
class B : public A {};

class Foo {
public:
    void Bar(A& a) {
        // do something
    }
    void Bar(B& b) {
        Bar(static_cast<A&>(b));
        // do B specific stuff
    }
};

最初的问题是正确的:“我后来调用了只有B具有的函数”-在这种情况下重载将如何工作?
Marcin Gil

当您用A呼叫Bar时,不会发生B事务。当用B调用Bar时,可以调用仅存在于B上的方法。您阅读原始问题了吗?Bar是他的“我正在重写一个接受类型为A的对象作为参数的函数”
jmucchiello

7
这不适用于动态多态性,我怀疑提问者正在使用它。C ++不能基于参数的运行时类选择重载,而只能基于编译时类型。
史蒂夫·杰索普

1

如果可以访问boost库,则可能需要使用type_id_with_cvr()函数,该函数可以提供数据类型而无需删除const,volatile和&&&&修饰符。这是C ++ 11中的一个简单示例:

#include <iostream>
#include <boost/type_index.hpp>

int a;
int& ff() 
{
    return a;
}

int main() {
    ff() = 10;
    using boost::typeindex::type_id_with_cvr;
    std::cout << type_id_with_cvr<int&>().pretty_name() << std::endl;
    std::cout << type_id_with_cvr<decltype(ff())>().pretty_name() << std::endl;
    std::cout << typeid(ff()).name() << std::endl;
}

希望这是有用的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.