C++ - typeid()用于派生类时未返回正确类型

8
也许我对继承的工作方式有所误解,但这是我的问题:
我有一个Option类,以及从它派生的RoomOption类。我还有另一个Room类,它包含一个shared_ptr的向量。在main函数中,我将一个RoomOption添加到该向量中。然后,使用typeid()检查类型,它告诉我它是一个Option。根据我所读的,typeid应该返回派生类型,并且shared_ptr不会导致切片,所以我不知道自己做错了什么。
下面是代码:
Room.h:
vector<shared_ptr<Option> > options;
void addOption(shared_ptr<Option>);
shared_ptr<Option> getOption(int);

Room.cpp:

void Room::addOption(shared_ptr<Option> option){
    options.push_back(option);
}

shared_ptr<Option> Room::getOption(int i){
    return options[i];
}

主函数:

shared_ptr<Room> outside(new Room(0, "", ""));
outside->addOption(shared_ptr<RoomOption>(new RoomOption(0, "Go inside", hallway)));
cout<<typeid(player->getRoom()->getOption(0)).name().get()<<endl; 
//This line prints "class std::tr1::shared_ptr<class Option>

我想到可能在添加或获取选项时,由于返回/参数类型的原因,RoomOption被转换为Option。如果是这样,那么我该如何存储多种类型的向量呢?还是我理解错了? =\


3
你需要获取共享指针所包含对象的类型id,尝试获取这个共享指针所指向的对象的类型id。 - Mat
使用dynamic_cast检查类型。此外,您可以获取shared_ptr的typeid。 - Sebastian Hoffmann
3个回答

18

typeid 在多态类型(至少有一个虚函数的类)和非多态类型上表现不同:

  • 如果类型是多态的,对应的 typeinfo 结构体在运行时被确定(通常使用虚表指针实现,但这是一种实现细节)

  • 如果类型不是多态的,对应的 typeinfo 结构体在编译时被确定

在您的情况下,您实际上有一个多态类 Option,但是 shared_ptr<Option> 本身并不是多态的。它基本上是一个容器,包含一个 Option*。在 Optionshared_ptr<Option> 之间绝对没有继承关系。

如果想获得实际类型,需要先使用 Option* shared_ptr<Option>::get() 函数从其容器中提取真正的指针:

Option * myPtr = player->getRoom()->getOption(0).get();
cout << typeid(*myPtr).name(); << endl;

或者,另一种方式(其实是完全相同的):

Option& myPtr = *player->getRoom()->getOption(0);
cout << typeid(myPtr).name(); << endl;

我会删除对vtable指针的引用,因为它是一个令人困惑的实现细节。 - Matthieu M.
注意:您也可以直接使用operator*获取底层Option的引用,而不是先使用get,然后在指针上使用* - Matthieu M.
1
我想澄清一下,真正的指针存储在shared_ptr<>实例中。使用引用可能会让OP认为shared_ptr<>是一个指针,而它只是一个试图模仿指针行为的容器(这就是为什么typeid()对他不起作用的原因)。我会将其作为另一种选择添加进去。 - Frédéric Terrazzoni

3
首先,您需要获取shared_ptr的typeid。
然后,您应该使用dynamic_cast而不是typeid。例如:
if (dynamic_cast<RoomOption*>(player->getRoom()->getOption(0).get()) != 0){
    cout << "Its a RoomOption!" << endl;
}

成功了!感谢提供的例子。找到一个简单的使用动态转换检查类型的示例真是异常困难啊O.o。 - Jean Finley
请注意,dynamic_cast 被认为是一种非常昂贵的操作(因为您的代码需要查找RTTI信息)。因此,在时间敏感的代码中不应频繁使用它。 - Sebastian Hoffmann
@SebastianHoffmann,typeid 也使用运行时类型信息(RTTI)。 - c z

1

shared_ptr<Option> 指向的对象类型是其值的一部分,而不是其类型。因此,这行代码是错误的:

cout<<typeid(player->getRoom()->getOption(0)).name()<<endl; 

您需要这个:

cout<<typeid(player->getRoom()->getOption(0).get()).name()<<endl; 

或者:

cout<<typeid(*(player->getRoom()->getOption(0))).name()<<endl; 

typeid 的作用是告诉你传递给它的实际类型。你传递了一个 shared_ptr<Option>。它不会查看对象内部包含的内容。


谢谢。但仍然返回“Option*”。 - Jean Finley

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接