C++基类引用指向派生类为何调用基类虚函数?异常切片疑问
C++ 问题解答
问题1:为何指向派生类对象的基类类型引用会调用基类的虚函数?
正常情况下,基类引用指向派生类对象时,调用虚函数应该触发派生类的覆盖版本。如果实际调用了基类的虚函数,通常是以下原因之一:
- 基类函数未声明为
virtual:如果基类的函数没有virtual修饰,调用会采用静态绑定,编译器根据引用的静态类型(基类)直接调用基类函数,无视实际指向的派生类对象。 - 派生类未正确覆盖虚函数:派生类的函数签名必须和基类虚函数完全一致(返回值支持协变、参数列表、const/volatile限定符等都要匹配),否则派生类的函数是全新的独立函数,不构成覆盖。比如基类函数是
virtual void func() const,派生类写成void func(),就不会覆盖基类的虚函数。 - 覆盖失败未被发现:如果没有使用
override关键字强制检查覆盖,签名不匹配的错误可能被隐藏,导致程序依然调用基类函数。
问题2:异常抛出中throw e与throw *p的类型表现差异
代码片段
// exception constructor #include <iostream> // std::cout #include <exception> // std::exception #include <cxxabi.h> #define PRINT_TYPENAME(f) std::cout << abi::__cxa_demangle(typeid(f).name(), 0, 0, &status) << std::endl; struct ooops : std::exception { const char *what() const noexcept { return "Ooops!\n"; } }; int main() { ooops e; std::exception *p = &e; // demangle the typeid char *realname; int status; PRINT_TYPENAME(p); PRINT_TYPENAME(*p); try { throw e; // throwing copy-constructs: ooops(e) } catch (std::exception &ex) { std::cout << ex.what(); } try { throw *p; // throwing copy-constructs: std::exception(*p) } catch (std::exception &ex) { std::cout << ex.what(); } return 0; }
输出
std::exception* ooops Ooops! std::exception
差异原因分析
typeid(*p)输出ooops的原因:std::exception是含有虚函数的多态类,typeid作用于多态类的左值表达式时,会动态识别对象的实际类型,所以*p虽然静态类型是std::exception,但实际指向ooops对象,因此typeid(*p)返回ooops的类型信息。throw e与throw *p的差异:
C++中抛出异常时,会根据表达式的静态类型创建异常对象,这会触发对象切片:
throw e:e的静态类型和动态类型都是ooops,因此创建的异常对象是ooops类型。catch块中用std::exception&捕获时,由于多态特性,调用的是ooops重写的what(),输出Ooops!。throw *p:*p的静态类型是std::exception,即使它实际指向ooops对象,编译器也会按std::exception类型复制对象,切片掉派生类ooops的部分,最终异常对象是std::exception类型。此时catch块调用的是基类的what(),输出std::exception。
内容的提问来源于stack exchange,提问作者pikalee
相关产品推荐
相关产品推荐

