假设我有一个名为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?
最佳答案
您正在像这样检索对象:
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
中可用的函数。由于编译器正在声明已声明变量的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
块。关于c++ - 在父类指针的映射中存储子类指针,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/43462857/