前言:
最近使用虚函数的时候遇到了一个坑,折腾了挺久,后来查了一些资料才搞明白,遂记录一下。
什么是虚函数
假如有两个类A、B。B继承A,那么这个时候,A类的指针类型可以指向B的对象。
class A{ public: void say(){ std::cout << "A" << std::endl; } }; class B:public A{ public: void say(){ std::cout << "B" << std::endl; } }; A* a = new B();
这个时候称a的静态类型是A,动态类型是B。
那么这里测试一下将a的静态类型转换为B,然后分别调用say()函数,会发生什么?
A* a = new B(); B* b = dynamic_cast<B*>(a); a.say(); b.say();
输出结果:
A
B
B
这里我们知道a和b实际上是指向了同一个对象,但是为什么调用say()函数却发生了不同的结果,是因为普通成员函数的调用,是由静态类型决定的。
然后我们给say()函数加上virtual(虚函数)声明
class A{ public: virtual void say(){ std::cout << "A" << std::endl; } }; class B:public A{ public: void say(){ std::cout << "B" << std::endl; } }; A* a = new B(); B* b = dynamic_cast<B*>(a); a->say(); b->say();
输出:
B
B
B
这个时候调用的都是B类的say()函数了,这就是虚函数的特性,会根据对象的动态类型来选择调用哪一个版本的say()函数,也称之为动态绑定。
虚函数不要带缺省参数
来说一下我遇到的坑,先将前面的代码稍微修改一下。
class A{ public: virtual void say(const std::string& str = "A:"){ std::cerr << str << "A" << std::endl; } }; class B :public A{ public: void say(const std::string& str = "B:"){ std::cout << str << "B" << std::endl; } }; A* a = new B(); B* b = dynamic_cast<B*>(a); a->say(); b->say();
结合虚函数的特性,你一定会下意识的觉得输出应该是两个B:B,但现实往往很残酷,我得到了这样的输出。
A:B
B:B
B:B
这似乎是我的知识盲区了,我翻阅了一些资料,表明这确实就是C++的特性(确定不是bug?),在虚函数有缺省参数的时候,调用函数的版本根据对象的动态类型决定,但是参数的默认值却是由静态类型决定的。
文章评论