简介
我有以下形式的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 Derived
或Derived Base
的输出,因为derived
是std::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。然后返回一个引用并销毁最后一个引用。
通过引用检查值将是未定义的行为。
所以你会有任何行为。有时你想要什么,有时不想要。