面向对象程序设计概述
面向对象程序设计基于三个基本概念:数据抽象(封装)、继承和动态绑定(多态)。
继承和多态对程序的编写有两方面的影响:
- 我们可以更容易地定义与其他类相似但不完全相同的类。
- 在使用这些彼此相似的类编写程序时,我们可以在一定程度上忽略掉它们的区别。
例如:输掉中不同书籍的定价策略可能不同:有的书籍按原价销售,有的打折销售等等。面向对象的程序设计适用于这类应用。
1.继承
通过继承联系在一起的类构成一种类似于树的层次关系。通常在层次关系的根都有一个基类(父类),其他类则直接或间接从基类继承而来,这些继承得到的类称为派生类(子类)。基类负责定义在层次关系中所有类共同拥有的成员,每个派生类定义拥有各自特有的成员。
例如:根据刚才的定价策略,我们首先定义了一个名为Quote的类作为基类表示按原价销售的书籍,Bulk_quote的类作为派生类表示可以打折销售的书籍。这些类包含两个函数:isbn()表示书籍的ISBN编号,netPrice()表示实际出售的价格。
class Quote {
public:
Quote() = default;
Quote(const string &s, double d):_isbn(s), _price(d){}
virtual double netPrice(int n) const {
return _price * n;
}
private:
string _isbn = "x";
protected:
double _price = 0.0;
};
class BulkQuote : public Quote { //公共继承了Quote
public:
BulkQuote() = default;
BulkQuote(const string &s, double d, int b, double dis):
Quote(s, d), _minBuy(b), _discount(dis){}
double netPrice(int n) const override {
if (n > _minBuy) {
return n * _discount * _price;
}
return n * _price;
}
private:
int _minBuy; //最低购买数量
double _discount; //小数表示的折扣额
};
①虚函数
基类将类型相关的函数(基类和子类不同的函数)和派生类不做改变直接继承的函数区别对待。对于基类希望它的派生类各自定义适合自身的版本的函数,此时基类就定义为虚函数,就像上述的netPrice函数一样。
派生类必须在其内部对所有重新定义的虚函数进行声明,派生类可以显式地注明它将使用哪个成员函数改写基类地虚函数,具体措施是在该函数地形参列表之后增加一个override关键字。
②类派生列表
派生类不许通过使用类派生列表明确指出它从哪个基类继承而来。
类派生列表的格式为:一个**冒号紧跟分隔的基类列表,其中每个基类前面可以有访问说明符。**如上述的:public Quote.
2.动态绑定(多态)
通过使用多态,我们能对同一段代码分别处理Quote和BulkQuote地对象。
如:当要购买地书籍和购买的数量都已知时,下面的函数负责打印总的费用:
//计算并打印销售额给定数量的某种书籍所得的费用
double printTotal(ostream &os, const Quote &item, int n) {
//根据传入item的形参的对象类型调用netPrice函数
double ret = item.netPrice(n);
os << "ISBN:" << item.isbn()
<< "sold:" << n << "total due:" << ret << endl;
return ret;
}
int main() {
Quote basic("abc", 100);
QuoteBulk bulk("abcd", 100, 5, 0.8);
printTotal(cout, basic, 20); //调用Quote的netPrice
printTotal(cout, bulk, 20); //调用BulkQuote的netPrice
return 0;
}
//输出结果为:
ISBN:abc sold:20 total due:2000
ISBN:abcd sold:20 total due:1600
当我们使用基类的引用或指针调用一个虚函数时会发生动态绑定,动态绑定发生在运行阶段。