什么会导致C++对象的类型信息在从函数返回后发生更改?

  • 本文关键字:返回 函数 信息 C++ 类型 对象 c++
  • 更新时间 :
  • 英文 :


简介

我有以下形式的C++17代码:

Base& makeDerived(int val) {
std::shared_ptr<Derived> derived = secondLayer(val);
std::cout << typeid(derived).name() << std::endl;
return *derived;
}
int main(void) {
Base& derived = makeDerived(7);
std::cout << typeid(derived).name() << std::endl;
return 0;
}

我可以把这个代码打印出来:

Derived
Derived

Derived
Base

Derived和Base的不同实现,但我不明白为什么会发生这种情况。

补充说明

我提出这个问题的实际代码是:Derived, Base,但粘贴起来太长了,而且我没有被授权这样做。我不知道如何将其提炼成一个有特点的例子,所有的努力似乎都在打印Derived, Derived,而我无法将其提炼出来的事实基本上就是我的问题所在,因为我不明白为什么它会有所不同。

在我关心的所有实现中,Derived都是Base的后代。

问题

什么会导致基类的后代在退出函数后将其类型信息更改为基类本身

不清楚如何获得Derived DerivedDerived Base的输出,因为derivedstd::shared_ptr<Derived>。无论如何,这里有一个与您的代码(很可能)存在相同问题的最小示例:

#include <iostream>
#include <memory>
struct Base { virtual ~Base() = default; };
struct Derived : Base {};
std::shared_ptr<Derived> secondLayer(int) { return std::make_shared<Derived>(); }
Base& makeDerived(int val) {
std::shared_ptr<Derived> derived = secondLayer(val);
std::cout << typeid(derived).name() << std::endl;
return *derived;
}
int main(void) {
Base& derived = makeDerived(7);
std::cout << typeid(derived).name() << std::endl;
}

可能输出:

St10shared_ptrI7DerivedE
4Base

我计划在这里发布一个免责声明,说明您应该如何注意警告和使用编译器的检测功能。然而,gcc使用-Wall -Werror默默地吞下了这段代码,而-fsanitize-address-fsanitize-undefined都没有帮助。我会认为这是一个bug。

然而,return *derived正在返回对对象的引用,一旦函数返回,该对象就会被销毁。在main中使用该引用会导致未定义的行为。

您可以在以下链接上阅读有关此答案的更多信息:https://en.cppreference.com/w/cpp/language/typeid

Base和Derived类的实现可以使用不同的方法,而在typeid的使用上可以有所不同的是多morphic或非多态继承。

从拥有虚拟方法的基类派生的类被认为是多态继承。在多态继承的情况下,typeid能够识别实例的真实类型(即使在您的情况下是在基类的引用中强制转换它)。

从不拥有任何虚拟mehotd的基类派生的类被认为是非多态继承,typeid无法识别您正在使用的实例的真实类型,在您的情况下,将返回引用类型base&的std::type_info;。

您创建了一个共享ptr。然后返回一个引用并销毁最后一个引用。

通过引用检查值将是未定义的行为。

所以你会有任何行为。有时你想要什么,有时不想要。

最新更新