C++静态与动态决议:虚函数调用规则及相关代码技术问询
C++虚函数决议:对象调用vs指针/引用调用的核心区别
Hey there!咱们来好好拆解你提到的这个C++虚函数规则,结合你给出的代码片段把这个逻辑捋明白~
首先先明确你说的核心规则(这可是C++多态的关键知识点哦):
使用对象调用虚函数始终会进行静态决议,仅通过指针或引用调用时才会触发动态决议
先把这个规则的本质说透
C++的多态依赖动态绑定,但动态绑定不是随便触发的——只有当你通过指针或者引用调用虚函数时,才会在运行时去判断实际指向的对象类型,然后调用对应的函数。
而如果直接用对象(注意,不是指针/引用)调用虚函数,编译器在编译阶段就100%确定了这个对象的类型,毕竟对象本身的类型是固定死的,哪怕是子类对象赋值给基类对象发生切片,基类对象的类型还是基类,所以直接就做静态绑定,编译时就把函数调用给确定了。
结合你的代码片段来分析(我补全了常见的多态场景)
先把你给的代码补成完整的多态示例,方便咱们看效果:
#include <iostream> class Mainclass{ protected: double length{1.0}; double width{1.0}; double height{1.0}; public: Mainclass(double lv, double wv, double hv):length{lv}, width{wv}, height{hv}{ std::cout<<"Three args box ran"<<std::endl; } // 把showVolume设为虚函数,这是多态的前提 virtual void showVolume() const{ std::cout << "Mainclass Volume: " << length * width * height << std::endl; } }; // 定义一个子类,重写showVolume class Subclass : public Mainclass{ public: Subclass(double lv, double wv, double hv) : Mainclass(lv, wv, hv) {} void showVolume() const override{ std::cout << "Subclass Volume: " << length * width * height * 2 << std::endl; } };
情况1:直接用对象调用(静态决议)
看这段代码的运行结果:
int main(){ Subclass sub(2, 3, 4); Mainclass mainObj(2, 3, 4); sub.showVolume(); // 编译时就确定调用Subclass的showVolume mainObj.showVolume(); // 编译时确定调用Mainclass的showVolume // 哪怕把子类对象赋值给基类对象(发生切片) Mainclass slicedObj = sub; slicedObj.showVolume(); // 还是调用Mainclass的showVolume!因为slicedObj是Mainclass类型的对象 }
这里的核心是:对象的类型是编译期就能确定的,编译器根本不需要等到运行时,直接把函数调用绑定到对应类的实现上。
情况2:用指针/引用调用(动态决议)
再看这段:
int main(){ Subclass sub(2, 3, 4); Mainclass* ptr = ⊂ Mainclass& ref = sub; ptr->showVolume(); // 运行时才确定调用Subclass的showVolume ref.showVolume(); // 同样是运行时动态绑定到Subclass的实现 }
这时候指针/引用的静态类型是Mainclass*/Mainclass&,但它们实际指向的是Subclass对象。编译器在编译期没法知道这个指针/引用最终指向的是什么类型的对象,所以会在运行时通过虚函数表(vtable)找到实际对象对应的函数实现,这就是动态决议。
最后再总结下为啥有这个区别
- 对象本身是“完整”的实体,它的类型是固定的,编译器能直接确定要调用哪个函数。
- 指针/引用只是一个“指向对象的工具”,它们的静态类型和实际指向对象的动态类型可能不一样,这时候就需要动态绑定来实现多态的效果。
内容的提问来源于stack exchange,提问作者juztcode
相关产品推荐
相关产品推荐

