OOP:概述
对于某些函数,基类希望它的派生类各自定义适合自身的版本,此时基类就将这些函数声明成虚函数。
当我们使用基类的引用或指针调用一个虚函数时将发生动态绑定,又称运行时绑定。
定义基类和派生类
基类通常都应该定义一个虚析构函数,即使该函数不执行任何实际操作也是如此。
virtual ~Quote() = default;
因为在派生类对象中含有与其基类对应的组成部分,所以我们能把派生类的对象当成基类对象来使用,而且我们也能将基类的指针或引用绑定到派生类对象中的基类部分上。
Quote item; // 基类对象
Bulk_quote bulk; // 派生类对象
Quote *p = &item; // p 指向 Quote 对象
p = &bulk; // p 指向 bulk 的 Quote 部分
Quote &r = bulk; // r 绑定到 bulk 的 Quote 部分
这种转换称为派生类到基类的类型转换。这种自动类型转换只对指针或引用类型有效,在派生类类型和基类类型之间不存在这样的转换,即在对象之间不存在类型转换。
如果基类定义了一个静态成员,则在整个继承体系中只存在该成员的唯一定义。不论从基类中派生出多少个派生类,对于每个静态成员来说都只存在唯一的实例。
如果不希望其它类继承该类,可以在类名后跟一个关键字 final。
虚函数
当我们使用基类的引用或指针调用一个虚成员函数时会执行动态绑定。因为我们直到运行时才能知道到底调用了哪个版本的虚函数,所以所有虚函数都必须有定义。
虚函数也可以有默认实参,如果某次函数调用使用了默认实参,则该实参值由本次调用的静态类型决定。换句话说,如果我们通过基类的引用或指针调用函数,则使用基类中定义的默认实参。
抽象基类
和普通的虚函数不一样,纯虚函数无需定义。
double net_price(std::size_t) const = 0; // =0 只能出现在类内部的虚函数声明语句处
含有(或者未经覆盖直接继承)纯虚函数的类是抽象基类,我们不能创建抽象基类的对象。
访问控制与继承
public 继承 | protected 继承 | private 继承 | |
---|---|---|---|
父类的 public 成员 | public | protected | private |
父类的 protected 成员 | protected | protected | private |
父类的 private 成员 | 不可见 | 不可见 | 不可见 |
struct 与 class 关键字定义的类具有不同的默认访问说明符,类似的,默认情况下,使用 class 关键字定义的类的派生类是私有继承的,使用 struct 关键字定义的派生类是公有继承的。
构造函数与拷贝控制
虚析构函数
当我们 delete 一个动态分配的对象的指针时将执行析构函数。如果该指针指向继承体系中的某个类型,则有可能出现指针的静态类型与被删除对象的动态类型不符的情况,所以要通过在基类中将析构函数定义成虚函数以确保执行正确的析构函数版本。
class Quote {
public:
// 如果我们删除的是一个指向派生类对象的基类指针,则需要虚析构函数
virtual ~Quote() = default; // 动态绑定析构函数
};