基类负责定义在层次关系中所有类共同拥有的成员,而每个派生类定义各自持有的成员。
基类希望它的派生类各自定义适合自身的版本,此时基类就将这些函数声明成虚函数。
class Quote{
public:
std::string isbn() const;
virtual double net_price(std::size_t n) const;
};
派生类必须通过使用类派生列表明确指出它是哪个基类继承而来。
类派生列表的形式:首先是一个冒号,后面紧跟以逗号分隔的基类列表,其中每个基类前面有访问说明符:
class Bulk_quote : public Quote{
public:
double net_price(std:size_t) const override;
};
派生类必须在其内部对所有重新定义的虚函数进行声明。基类中说明了虚函数后,派生类中对应的函数可以不必说明为虚函数。
因为Bulk_quote 在派生类列表使用了public关键字,因此完全把Bulk_quote的对象当成Quote的对象来使用。
C++新标准允许派生类显示地注明它将使用哪个成员函数改写基类的虚函数,在该函数的形参列表之后增加override关键字。
基类的定义
class Quote{
public:
Quote()=default;
Quote(const std::string &book, double sales_price):
bookNo(book),price(sales_price){}
std::string isbn() const(return bookNo;)
virtual double net_price(std::size_t n) const
{return n*price;}
virtual ~Quote()=default;
private:
std::string bookNo;
protected:
double price=0.0;
};
类中如果自己定义了构造函数,那么系统不会自动生成默认构造函数,需要自己定义一个默认构造函数,语法为构造函数=default;
继承关系中根节点的类通常都会定义一个虚析构函数。
动态绑定:可以一定程度忽略相似类型的区别,而以统一的方式使用他们的对象。
实现方式:定义基类的指针或引用。根据指针或引用接受的对象的类别不同,可以用相同的语句形式调用不同的虚函数版本。
class A{
virtual f1(int x)
{print "A!\n";}
};
class B:public A{
virtual f1(int x)
{print "B!\n";}
};
A a;
B b;
A *pa=&a;
pa.f1(1);
pa=&b;
pa.f1(1);
Reult: A! B!
虚函数的作用就是实现“动态联编”,也就是在程序的运行阶段动态地选择合适的成员函数
和类的静态成员函数与非类的成员函数相比,虚函数的效率较低
要正确的实现虚函数,只能用一个基类的指针或者引用来指向派生类对象