玩命加载中 . . .

15.2-定义基类和派生类


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_caststatic_cast进行转换

对象间不存在类型转换
Bulk_quote bulk;
Quote item(bulk);   // 调用Quote::Quote(const Quote&)构造函数
item = bulk;    // 调用Quote::operator=(const Quote&)

文章作者: kunpeng
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 kunpeng !
  目录