定不定义
普通成员函数只要没用到可以不写定义,但虚函数只要在主函数被用到,所有父类和子类必须都定义该函数,因为编译器只有在运行时才能确定用到哪个函数。
调用虚函数
指针或引用运行时解析
有两种类型:动态和静态。
这里有动态类型和静态类型,只有在运行时才知道动态类型。
Quote base;
print_total(cout, base, 10); //调用Quote::net_price()
Bulk_quote derived;
print_total(cout, derived, 10); //调用Bulk_quote::net_price()
普通类型编译时解析
只有一种类型:只有静态了。
如果我们通过普通类型调用虚函数,就只会调用静态类型的函数。
base = derived; //动态和静态类型一致
base.net_price(20); //调用Quote::net_price
调用普通函数
测试都是调用基类的函数。
虚函数重写叫覆盖,普通函数重写叫隐藏。
class Base
{
public:
void f()
{
cout<<"Base"<<endl;
}
};
class Derived: public Base
{
public:
void f() //报隐藏非虚函数
{
cout<<"Derived_f"<<endl;
}
void g()
{
cout<<"Derived_g"<<endl;
}
};
int main() {
Base base;
Derived derived;
Base *p = &base;
p ->f(); //输出Base
p ->g(); //xxx ,报class Base无此函数
p = &derived;
p ->f(); //输出Base
p ->g(); //xxx ,报class Base无此函数
return 0;
}
口诀-Primer537
普通函数普通类型静态时,虚函数指针或引用运行时,普通类型还是静态时。
只有虚函数和引用或指针这俩条件都满足,才会动态绑定。
关键字
virtual关键字的范围
只要声明为virtual,派生类就不用声明了。
虚函数叫覆盖,形参和返回值都要完全匹配。
override关键字
动机
有时我们写了相同名字的函数,但形参列表和想覆盖的虚函数不同,这时编译器视为新函数,但是其实是想覆盖,这是一个不小心的错误。可以加上override来帮助检查。
final关键字
既可以不让类继承,也可以不让函数覆盖
忽略虚函数机制
加上作用域就行。