你正在像这样检索你的对象:
Libitem* old_book = catalog[1];
这样,编译器只知道您有一个名为old_book 的变量,类型为指向Libitem。如果您自己单独阅读该特定行,您会注意到唯一的这是您和编译器在那一刻拥有的唯一信息。当你自己阅读这行代码的那一刻就是编译时间。看代码就知道什么了。
当程序实际运行时,该变量可能指向Book 类型的对象。但这只有在程序运行时才知道,因此 runtime。
名称查找发生在 C++ 的编译时。当您像这样在对象上调用函数时:
// Type of an_object is a_struct
a_struct an_object;
an_object.member_function();
编译器将查看a_struct 中可用的函数。由于编译器在变量已声明的结构中查找该名称,因此名称实际上是在编译时解析的。
让我们回到你的案例。你有一个指向Libitem 的指针。如果您尝试使用箭头访问其中的某些内容:
old_book->something
为了解析something 是什么,编译器将在Libitem 内部查找它,因为old_book 的类型是指向Libitem 的指针。即使指针指向子类的实例,编译器唯一知道肯定的是指向的对象的实际类型至少是Libitem。
现在,人类比编译器知道的更多。您知道指针old_book 指向Book 类的一个实例。您想访问Book 的成员。
为此,您必须明确告诉编译器您要使用来自子类的成员。为此,您的变量必须是Book 类型,因此编译器将在适当的类中查找。为此,您可以将变量转换为另一种类型。由于您将变量转换为层次结构中较低的类,因此称为向下转换。在这种情况下,我们可以使用的类型转换是 dynamic_cast,它会在运行时查看指针所指向的实例的实际类型:
if (Book* the_old_book = dynamic_cast<Book*>(old_book)) {
// We can use the_old_book here, which his type is Book!
} else {
// The cast failed, the real for of the variable is not Book,
// and the_old_book points to nullptr
}
如您所见,我们创建了一个名为the_old_book 的新指针,它由强制转换的结果初始化。如果old_book 指向的实例的真实类型确实不是Book,则强制转换将失败并返回nullptr。由于这发生在运行时,我们必须使用运行时分支 if 来验证我们的新变量。如果转换失败,执行的块将是 else 块。