第15章 面向对象的程序设计
2017-07-07 本文已影响0人
菜鸡也会飞
一、OOP:概述
- 面向对象程序设计的核心思想是数据抽象、继承和动态绑定
- 数据抽象:将类的接口与实现分离;
- 继承:定义相似的类型并对其相似关系建模;
- 动态绑定:在一定程度上忽略相似类型间的区别,而使用统一的方式使用它们的对象
- 通过继承联系在一起的类构成一种层次关系。
- 基类:层次根部,其他类均直接或者间接从基类继承而来。定义继承关系中所有类共有的成员
- 派生类:继承得到的类。定义各自特有的成员
- 虚函数:对于某些成员函数,基类希望它的派生类各自定义适合自身的版本,此时基类就将这些函数声明为虚函数。
- 类派生列表
- 派生类必须使用派生类列表指出它继承自那个(或哪些基类)
- 形式:先是一个冒号,然后跟逗号分隔的基类列表
- 每个基类前面可以有访问说明符
- 动态绑定
- 通过使用动态绑定,可以用同一段代码分别处理具有继承关系的不同对象(Quote和Bulk_Quote)
- eg:
double print_total(const Quote &item, size_t n) {
double ret = item.net_price(n);
}
- 由于形参是对基类的引用,所以既能使用基类调用,也能用派生类调用
- 运行时绑定:使用哪个版本的price()函数,由运行时函数的实参是Quote类型还是Bulk_Quote类型决定
- 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;
};
二、定义基类和派生类
- 定义基类
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访问说明符来表明。
- 定义派生类
三、虚函数
四、抽象基类
- 设计动机
如果希望书店程序能够支持多种折扣方式策略,可以定义一个新的Disc_Quote类来支持不同的折扣策略。该类表示的是一本打折书籍的通用概念,而非某种具体的折扣策略,因此在其中定义net_price()函数是无意义的,但是如果不定义net_price()函数,其派生类会调用Quote的price函数。 - 纯虚函数
- 为解决上述问题,因此在Disc_Quote类中将price函数定义为纯虚函数。纯虚函数明确告诉用户,当前price()函数没有实际意义。
- 纯虚函数无需定义,通过在函数体位置书写‘=0’表示
- 可以为纯虚函数提供定义,不过函数体必须定义在类的外部,也就是说不能在类内为一个=0的函数提供函数体
double net_price(std::size_t) const = 0;
- 抽象基类
- 含有纯虚函数的类是抽象基类
- 不能(直接)创建一个抽象基类**→强调不能直接创建,是因为当创建Disc_Quote的派生类的对象时,会间接创建一个Disc_Quote类的对象。
- Disc_Quote的派生类必须给出自己的net_price()函数定义,否则它仍然是一个抽象基类→因此继承了Disc_Quote类的纯虚函数net_price()
- 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类的构造函数
- 派生类构造函数中使用基类的构造函数初始化基类成员
- 重构
- 在Quote和Bulk_Quote的继承关系之间添加了Disc_Quote类
- 重构负责重新设计类的体系以便将操作和/或数据从一个类移动到另一个类中;
- 重构即使改变了继承体系,使用了Quote或者Bulk_Quote类的代码无需变更;
- 类被重构,意味着需要重新编译代码