)
本文有两处小细节可以更严谨静态类型语言之外的补充下文里说在使用基类指针/引用时才会涉及动态类型这是常见的实用说法。严格按标准glvalue 的动态类型指它所引用的最派生对象的类型只是对非多态类来说你无法通过typeid/dynamic_cast观察到它。Derived::bar()的写法它隐藏hide了Base::bar而不是重写。这个例子本来就是想演示非虚函数按静态类型选择所以没问题只是如果想学习值得明确说一句这是隐藏不是覆盖。C 的静态类型和动态类型C 是静态类型语言变量、表达式、函数参数等的类型在编译期就确定了这叫静态类型。同时C 通过继承 虚函数支持运行期多态所以在使用基类指针/引用操作对象时才会涉及动态类型对象在运行时实际是什么类型。看例子#includeiostream#includetypeinfoclassBase{public:virtual~Base()default;// 有虚函数Base 是多态类型virtualvoidfoo()const{std::coutBase::foo\n;}voidbar()const{std::coutBase::bar\n;}};classDerived:publicBase{public:voidfoo()constoverride{std::coutDerived::foo\n;}voidbar()const{std::coutDerived::bar\n;}voidextra()const{std::coutDerived::extra\n;}};intmain(){Derived d;Base*pd;// p 的静态类型是 Base*// *p 的静态类型是 Base// *p 的动态类型是 DerivedBaserd;// r 的静态类型是 Base// r 的动态类型是 Derivedp-foo();// 输出 Derived::foo虚函数按动态类型调用p-bar();// 输出 Base::bar非虚函数按静态类型调用r.foo();// 输出 Derived::foo// p-extra(); // 编译错误编译器只看静态类型 Base*看不到 extraif(typeid(*p)typeid(Derived)){std::coutp 指向的对象动态类型是 Derived\n;}if(Derived*qdynamic_castDerived*(p)){q-extra();// 输出 Derived::extra}Base bd;// 对象切片b 的静态类型和动态类型都是 Baseb.foo();// 输出 Base::foo}核心区别概念静态类型动态类型确定时机编译期运行期依据变量/表达式声明类型指针/引用实际指向的对象类型用途类型检查、重载解析、模板、非虚函数调用虚函数分派、typeid、dynamic_cast例子Base* p的静态类型是Base*p指向Derived时*p的动态类型是Derived一句话总结静态类型是编译器看到的类型动态类型是运行时对象真正的类型。在 C 中动态类型主要通过虚函数和RTTI起作用。小写字母a的 ASCII 编码值是十进制97十六进制0x61八进制141二进制01100001补充大写字母A的 ASCII 值是65。在常见 C 实现中int(a) 97不过 C 标准本身不强制一定使用 ASCII但主流平台基本都是 ASCII/UTF-8 兼容。