第15章 面向对象的程序设计

一、OOP:概述

  1. 面向对象程序设计的核心思想是数据抽象、继承和动态绑定
  • 数据抽象:将类的接口与实现分离;
  • 继承:定义相似的类型并对其相似关系建模;
  • 动态绑定:在一定程度上忽略相似类型间的区别,而使用统一的方式使用它们的对象
  1. 通过继承联系在一起的类构成一种层次关系。
  • 基类:层次根部,其他类均直接或者间接从基类继承而来。定义继承关系中所有类共有的成员
  • 派生类:继承得到的类。定义各自特有的成员
  • 虚函数:对于某些成员函数,基类希望它的派生类各自定义适合自身的版本,此时基类就将这些函数声明为虚函数。
  1. 类派生列表
  • 派生类必须使用派生类列表指出它继承自那个(或哪些基类)
  • 形式:先是一个冒号,然后跟逗号分隔的基类列表
  • 每个基类前面可以有访问说明符
  1. 动态绑定
  • 通过使用动态绑定,可以用同一段代码分别处理具有继承关系的不同对象(Quote和Bulk_Quote)
  • eg:
double print_total(const Quote &item, size_t n) {
  double ret = item.net_price(n);
}
  • 由于形参是对基类的引用,所以既能使用基类调用,也能用派生类调用
  • 运行时绑定:使用哪个版本的price()函数,由运行时函数的实参是Quote类型还是Bulk_Quote类型决定
  1. eg:
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 n) const override;
};

二、定义基类和派生类

  1. 定义基类
class Quote {
  public:
    Quote() = default;
    Quote(const std::string& book, double sales_price):
               bookNo(book),price(sales_price) {}
    std::sting isbn() {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;
};

说明:

  • 基类必须区分两类成员函数:一类派生类直接继承不做改变,一类派生类定义自己的版本,对基类版本进行覆盖。后者就定义为虚函数。
  • 任何构造函数外的非静态函数,都可以是虚函数
  • 非虚函数解析发生在编译时,虚函数发生在运行时
  • 派生类虽然可以继承定义在基类中的成员,但派生类的成员函数无权访问基类的私有成员
  • Quote类的成员price,希望派生类能够访问,而禁止其它类访问,则用protected访问说明符来表明
  1. 定义派生类

三、虚函数

四、抽象基类

  1. 设计动机
    如果希望书店程序能够支持多种折扣方式策略,可以定义一个新的Disc_Quote类来支持不同的折扣策略。该类表示的是一本打折书籍的通用概念,而非某种具体的折扣策略,因此在其中定义net_price()函数是无意义的,但是如果不定义net_price()函数,其派生类会调用Quote的price函数
  2. 纯虚函数
  • 为解决上述问题,因此在Disc_Quote类中将price函数定义为纯虚函数。纯虚函数明确告诉用户,当前price()函数没有实际意义
  • 纯虚函数无需定义,通过在函数体位置书写‘=0’表示
  • 可以为纯虚函数提供定义,不过函数体必须定义在类的外部,也就是说不能在类内为一个=0的函数提供函数体
double net_price(std::size_t) const = 0;
  1. 抽象基类
  • 含有纯虚函数的类是抽象基类
  • 不能(直接)创建一个抽象基类**→强调不能直接创建,是因为当创建Disc_Quote的派生类的对象时,会间接创建一个Disc_Quote类的对象。
  • Disc_Quote的派生类必须给出自己的net_price()函数定义,否则它仍然是一个抽象基类因此继承了Disc_Quote类的纯虚函数net_price()
  1. eg
class Disc_Quote: public Quote {
  public:
    Disc_Quote() = default;
    Disc_Quote(const std::string& book, double price, std::size_t qty, double disc):
                Quote(book,price),quantity(qty),discount(disc) {}
    double net_price(std::size_t) const = 0;
  protected:
    std::size_t quantity = 0;
    double discount = 0.0;
};
class Bulk_Quote:public Disc_Quote{
  public:
    Bulk_Quote() = default;
    Bulk_Quote(const std::string& book, double price, std::size_t qty, double disc):
      Disc_Quote(book,price,qty,disc) {}
    double net_price(std::size_t) const override;
};

说明:

  • Bulk_Quote类的对象包含三个子对象:**一个(空的)Bulk_Quote部分,一个Disc_Quote子对象,一个Quote子对象
  • 派生类构造函数只初始化它的直接基类→Bulk_Quote类的构造函数将参数传递给Disc_Quote类的构造函数→Disc_Quote的构造函数又将实参传递给Quote类的构造函数
  • 派生类构造函数中使用基类的构造函数初始化基类成员
  1. 重构
  • 在Quote和Bulk_Quote的继承关系之间添加了Disc_Quote类
  • 重构负责重新设计类的体系以便将操作和/或数据从一个类移动到另一个类中;
  • 重构即使改变了继承体系,使用了Quote或者Bulk_Quote类的代码无需变更
  • 类被重构,意味着需要重新编译代码

五、访问控制和继承

你可能感兴趣的:(第15章 面向对象的程序设计)