虚函数的定义
在基类与派生类那里我们介绍了当我们使用基类的引用或指针调用一个虚成员函数的时候会执行动态绑定,因为我们在运行之前也并不知道会调用哪个版本的虚函数,因为我们只有在运行的时候才能确定我们将会调用哪一个版本的虚函数,所以我们在定义的时候必须给每一个虚函数提供定义。
对函数的调用在运行时才被解析
#include<iostream> using namespace std; class Quote { public: string book_ISBN;//书的编号 double price; Quote(const string& name,double p,string No):book_name(name),price(p),book_ISBN(No) { } virtual double net_price(size_t n) const { return n*price; } virtual string get_ISBN() const { return book_ISBN; } Quote(double p,string No):price(p),book_ISBN(No) {} virtual ~Quote()=default; private: string book_name; }; class Bulk_Quote:public Quote { private: size_t min_qry; double discount; string book_name; public: Bulk_Quote(const string& name,string book_ISBN,double price,size_t qry,double dis):Quote(price,book_ISBN),book_name(name),min_qry(qry),discount(dis) {} double net_price(size_t n) { if(n>=min_qry) price=price*discount; return n*price*discount; } string get_ISBN() const { return book_ISBN; } ~Bulk_Quote() {} }; void Total_price(ostream& os,const Quote& item,size_t n) { double ret=item.net_price(n); os<<"ISBN: "<<item.get_ISBN()<<endl; os<<"net_price: "<<ret<<endl; } int main() { Quote base("fengxu",10.78,"0-201-82470a-1"); Bulk_Quote derived("fengxu","0-201-82470a-1",10.78,10,0.7); Total_price(cout,derived,40); Total_price(cout,base,10); return 0; }
如上代码所示,虽然我们指定的类型时Quote的引用,但是当我们传进derived时,item会绑定在Bulk_Quote类型的对象上,这时调用则是Bulk_Quote::net_price,而传入base时,则是与Qutoe绑定,此时调用的则是Quote::net_price.
final和override关键词
在我们定义派生类的时候,有时候我们如果在派生类里面定义了一个名字和虚函数相同但是形参列表顺序不同的函数,在编译器看来这么写其实是合法的,但是这个函数和虚函数没有任何关系,这也意味着我们原先需要覆盖的虚函数并没有被覆盖,就实际要求来看这时候发生了错误。
这时候就要拿出我们的override关键词了,当我们用override标记了这个函数,如果这个函数并没有成功覆盖已经存在的虚函数,此时编译器就会报错:
class Quote { public: Quote() =default; Quote(double size_price,const string& number):size_price(size_price),m_number(number) { } string IseN() { return m_number; } virtual double Total_price(double size_price,int n) { return n*size_price; } virtual ~Quote() =default; private: string m_number; protected: double size_price=0.0; }; class Bulk_Qutoe:public Quote { public: Bulk_Qutoe()=default; Bulk_Qutoe(double,const string&,int,double); double Total_price(int book_number,double size_price) const override; private: double discount; int book_number; string book_name; };
class Bulk_Qutoe:public Quote { public: Bulk_Qutoe()=default; Bulk_Qutoe(double,const string&,int,double); double Total_price(int book_number,double size_price) const ; private: double discount; int book_number; string book_name; };
对于上述Bulk_Quote的俩种不同写法,前者会报错,而后者则是合法的,充分的说明了override的作用。
final关键词的作用则是当我们把某个函数标记为final后,之后所有试图覆盖该函数的操作都是非法的。·
虚函数和默认实参
和其他函数一样,虚函数也有默认实参,如果调用了默认实参。则实参值由本次调用的静态类型决定。
通俗一点来说,当我们使用基类的引用或者指针来调用函数的时候,此时使用的是基类的默认实参,哪怕当前使用的是派生类里面的函数也是这样,此时传进去的仍然是基类函数定义的默认实参,如果派生类重新定义使该函数被覆盖,此时用派生类的引用或者指针对函数进行调用,传递进去的就是派生类定义的默认实参。
回避虚函数的机制
在一些特定的场景下,我们希望对虚函数的调用不要进行动态绑定,而是我们可以自行决定我们使用虚函数的某一个特定的版本,这时候我们可以使用作用域运算符来实现执行虚函数的某一个特定版本,实例代码如下:
double undiscounted=basep->Quote::net_price(42);
这里baseP是一个指向基类的指针。
虚函数的回避机制常常用于派生类已经被覆盖的虚函数需要调用其基类版本,如果不使用回避机制会造成函数的无限递归。