所以说我有一个名为Libitem
的父类和一个名为Book
的子类。如果我将Book
的指针存储在Libitem
指针的映射中,当我再次尝试访问它时,是否会返回Book
的指针?像这样:
std::map<int, Libitem*> catalog;
Libitem* b = new Book();
catalog[1] = b;
Libitem* old_book = catalog[1]; //would old_book be a Book or a Libitem? In other word would it
//have all the function of a Book class?
答案 0 :(得分:2)
它就像任何其他多态指针:Libitem
的功能,但在方法中有Book
的任何虚拟覆盖。
但是:您将无法访问Book
的非虚方法,无论它是一个函数/方法,其名称与Libitem
中的任何方法完全不同,名称相同但参数不同签名到Libitem
或非虚拟中的方法,与Libitem
中的方法具有相同的名称和签名。
当然,如果你是一个强壮的人,你可以贬低(正如@SomeProgrammerDude指出的那样)。
答案 1 :(得分:2)
您正在检索您的对象:
Libitem* old_book = catalog[1];
有了这个,编译器只知道你有一个名为old_book
的变量指向Libitem
的变量。如果你自己只阅读那条特定的行,你会发现只有这是你和编译器当时唯一的信息。你自己读这行的那一刻就是编译时间。通过阅读代码可以了解的内容。
当程序实际运行时,该变量可能指向Book
类型的对象。但这仅在程序运行时才知道,因此,运行时。
名称查找在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
块。