C++不要重写带缺省参数的虚函数(virtual函数)

2021年3月31日 4490点热度 0人点赞 0条评论

前言:

最近使用虚函数的时候遇到了一个坑,折腾了挺久,后来查了一些资料才搞明白,遂记录一下。

什么是虚函数

假如有两个类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

这里我们知道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类的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

[啊这]

这似乎是我的知识盲区了,我翻阅了一些资料,表明这确实就是C++的特性(确定不是bug?),在虚函数有缺省参数的时候,调用函数的版本根据对象的动态类型决定,但是参数的默认值却是由静态类型决定的。

大脸猫

这个人虽然很勤快,但什么也没有留下!

文章评论