假设我有一个名为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/

10-11 23:05
查看更多