虚函数
动态绑定只有通过指针或引用调用虚函数时才会发生
派生类中虚函数的返回类型也必须与基类函数匹配,有一个例外,『当类的虚函数返回类型是类本身的指针或引用时,上述规则无效』一旦某个函数被声明为虚函数,则在所有派生类中它都是虚函数
一个派生类的函数如果覆盖了某个继承而来的虚函数,则它的形参类型必须与它覆盖的基类函数完全一致
final和override说明符
只有虚函数才能被覆盖,可以使用override
关键字来说明派生类中的虚函数
struct B {
virtual void f1(int) const;
virtual void f2();
void f3();
};
struct D1 : B {
void f1(int) const override; // 正确,f1与基类的f1匹配
void f2(int) override; // Err,B没有形如f2(int)的函数
void f3() override; // Err,f3不是虚函数
void f4() override; // Err,B没有名为f4的函数
};
可以把某个函数指定为final
,则之后任何尝试覆盖该函数的操作都将引发错误
struct D2 : B {
void f1(int) const final; // 不允许后续的其他类覆盖f1(int)
};
struct D3 : D2 {
void f1(int) const; // Err,D2已将f1声明成final
};
如果虚函数使用默认实参,则基类和派生类中定义的默认实参最好一致
回避虚函数的机制
强制调用基类的版本
// 强行调用基类中定义的函数版本而不管baseP的动态类型是什么
double undiscounted = baseP->Quote::net_price(42);
如果一个派生类虚函数需要调用它的基类版本,但是没有使用作用域运算符,则在运行时该调用将被解析为对派生类版自身的调用,从而导致无限递归
抽象基类
在声明语句中的分号之前书写=0,可以定义为纯虚函数
class Disc_quote : public Quote {
public:
Disc_quote() = default;
Disc_quote(const string& book, double price, size_t qty, double disc):
Quote(book, price), quantity(qty), discount(disc) {}
double net_price(size_t) const = 0;
protected:
size_t quantity = 0; // 折扣使用的购买量
double discount = 0; // 折扣
};
可以为纯虚函数提供定义,但必须定义在类的外部
含有(或未经覆盖直接继承)纯虚函数的类是抽象基类
// Disc_quote声明了纯虚函数,而Bulk_quote将覆盖该函数
Disc_quote discounted; // Err,不能创建抽象基类的对象
Bulk_quote bulk; // Yes
派生类构造函数只初始化它的直接基类,Bulk_quote
只需要调用它的直接基类Disc_quote
的构造函数就可以了
class Bulk_quote : public Disc_quote {
public:
Bulk_quote() = default;
Bulk_quote(const string& book, double price, size_t qty, double dissc):
Disc_quote(book, price, qty, disc) {}
double net_price(size_t) const override;
};