15.2 定义基类和派生类
class Quote {
public:
Quote() = default;
Quote(const string& book, double sales_price) :
bookNo(book), price(sales_price) {}
string isbn() const { return bookNo; }
virtual double net_price(size_t n) const {
return n * price;
}
virtual ~Quote() = default;
private:
string bookNo;
protected:
double price = 0.0; // 普通状态下不打折的价格
};
基类通常都应该定义一个虚析构函数,即使该函数不执行任何实际操作也是如此
protected
:派生类可以访问,普通对象不能访问
class Bulk_quote: public Quote {
public:
Bulk_quote() = default;
Bulk_quote(const string&, double, size_t, double);
double net_price(size_t) const override; // 重载基类的虚函数
private:
size_t min_qty = 0; // 使用折扣额的最低购买量
double discount = 0.0; // 以小数表示的折扣额
};
派生类向基类的类型转换
可以把派生类对象的指针用在需要基类指针的地方
Quote quote; // 基类对象
Bulk_quote bulk; // 派生类对象
Quote *p = &item; // 基类指针指向基类对象
p = &bulk; // 指向bulk的Quote部分
Quote &r = bulk; // 绑定到Quote部分
派生类必须使用基类的构造函数来初始化继承来的成员
Bulk_quote(const string& book, double p, size_t qty, double disc) :
Quote(book, p), min_qty(qty), discount(disc) {}
首先初始化基类的部分,然后按声明的顺序依次初始化派生类的成员
派生类可以访问基类的公有成员和受保护成员
double net_price(size_t cnt) const {
if (cnt >= min_qty) {
return cnt * (1 - discount) * price;
}
else return cnt * price; // price是基类的受保护成员
}
继承与静态成员
class Base {
static void statement();
};
class Derived : public Base {
void f(const Derived&);
};
void Derived::f(const Derived& derived_obj) {
Base::statement(); // 通过基类访问
Derived::statement(); // 通过派生类访问
derived_obj.statement(); // 通过对象访问
statement(); // 通过this对象访问
}
派生类的声明
声明中包含类名但是不包含它的派生列表,不需要说明从哪个基类继承而来
class Bulk_quote: public Quote; // 错误
class Bulk_quote; // 正确
一个类的基类,同时也可以是一个派生类
class Base {};
class D1: public Base {};
class D2: public D1 {};
如果不希望类被继承,可以加final
class Noderived final {};
class Base {};
class Last final : Base {}; // 正确
class Bad: Noderived {}; // 错误
class Bad2: Last {}; // 错误
类型转换与继承
使用基类的引用或指针时,实际上我们并不清楚所绑定的对象的真实类型
- 静态类型:编译时已知
- 动态类型:运行时才知道
double print_total(ostream& os, const Quote& item, size_t, n) {
// 根据传入的item形参的对象类型调用Quote::net_price
// 或者Bulk_quote::net_price
double ret = item.net_price(n);
os << "ISBN: " << item.isbn() << "#sold: " << n << "total due: " << ret << endl;
return ret;
}
print_total(cout, basic, 20); // 基类对象,调用Quote::net_price
print_total(cout, bulk, 20); // 派生类对象,调用Bulk_Quote::net_price
不存在从基类向派生类的隐式类型转换
Quote base;
Bulk_quote *bulkP = &base; // 错误,不能用派生类指针指向基类对象
Bulk_quote &bulkRef = base; // 错误,不能用派生类引用绑定基类对象
Bulk_quote bulk;
Quote *itemP = &bulk; // 正确,基类指针指向派生类对象
Bulk_quote *bulkP = itemP; // 错误,编译器只能通过检验静态类型来判断
// 派生类指针和基类指针指向派生类对象,看起来可以,但编译器还是报错
可以通过dynamic_cast
或static_cast
进行转换
Bulk_quote bulk;
Quote item(bulk); // 调用Quote::Quote(const Quote&)构造函数
item = bulk; // 调用Quote::operator=(const Quote&)