15.3 虚函数
通常情况下,如果我们使用某个函数,则不需要为该函数提供定义,只需提供声明即可。
但是我们必须为每一个虚函数都提供定义,而不管他是否被用到,因为编译器也无法确定到底会用到哪个虚函数。
对虚函数的调用可能在运行时才被解析
当某函数通过指针或引用调用时,编译器产生的代码知道运行时才能确定应该调用哪个版本的函数,被调用的函数是与绑定到指针或引用上的对象的动态类型相匹配的那一个。
但动态绑定只在通过指针或引用调用虚函数时才会发生,所以使用普通类型(非指针非引用)的表达式调用虚函数时,在编译时调用的版本就会确定下来。
base = derived; // 调用赋值运算符,不会发生动态绑定
base.net_price(20); // 调用Quote::net_price
派生类中的虚函数
一个派生类中的函数如果覆盖了某个继承而来的虚函数,则它的形参类型必须与它被覆盖的基类函数完全一致。
final和override说明符
C++11中允许我们使用override
关键字来说明派生类中的虚函数覆盖继承而来的虚函数,函数前面可以不用加virtual
。
将虚函数指定为final
表示该函数不能被覆盖。
void show() override {...} // 覆盖继承而来的虚函数
virtual show() final {...} // 此虚函数不能被覆盖
回避虚函数的机制
使用作用域运算符可以执行虚函数的某个特定版本。
baseP->Quote::net_price(42); // 该调用将在编译时完成解析
15.4 抽象基类
纯虚函数
一个纯虚函数无须定义,在声明处写个=0
即可,不用加virtual
关键字在前面。
相当于给所有派生类提供一个形式统一的接口,由各派生类覆盖实现自己的版本。
含有纯虚函数的类是抽象基类
含有(或者未经覆盖直接继承)纯虚函数的类是抽象基类。抽象类负责定义接口,而后续其它的类可以覆盖该接口。我们不能直接创建一个抽象基类的对象。
派生类构造函数只初始化它的直接基类
Bulk_quote的直接基类是Disc_quote,间接基类是Quote。
Bulk_quote的构造函数将它的实参传递给Disc_quote的构造函数,随后Disc_quote的构造函数继续调用Quote的构造函数。Quote的构造函数先首先初始化bulk的bookNo和price成员,当Quote的构造函数结束后,开始运行Disc_quote的构造函数并初始化quantity和discount的成员,最后运行Bulk_quote的构造函数。
Bulk_quote(const string &book, double price, size_t qty, double disc):
Disc_quote(book, price, qty, disc);