这三天主要是开始学习非常重要的第15章:面向对象程序设计。
15.1
这一节是面向对象编程(object oriented program OOP)概述。OOP中有三个最重要的概念:数据抽象(ADT)继承(inheriance),和动态绑定(dynamic binding)。继承是通过基类和派生类构成的层次关系。如果有成员函数需要传递下去需要将它在基类里声明为虚函数(virtual function)。第二个关键概念是动态绑定,由于派生类和基类存在一个嵌套关系,这就造成了静态类型和动态类型不匹配的问题,只有在运行时才能最终确定下来动态的类型到底是什么。以下为一个实例:
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 void debug() {
cout << "The book number is "<<isbn() << endl;
cout << "Single price is " << price << endl;
}
virtual ~Quote() = default;
private:
string bookNo;
protected:
double price = 0.0;
};
class Disc_Quote:public Quote{
public:
Disc_Quote() = default;
Disc_Quote(const string &book, double sales_price,size_t qty,double disc) :
Quote(book,price),quantity(qty),discount(disc){ }
virtual double net_price(size_t n) const = 0;
protected:
size_t quantity = 0;
double discount = 0.0;
};
class Bulk_Quote:public Disc_Quote{
public:
Bulk_Quote() = default;
Bulk_Quote(const string& book, double p, size_t qty, double disc):
Disc_Quote(book,p,qty,disc){}
double net_price(size_t cnt) const override
{
if (cnt >= quantity)
return cnt * (1 - discount)*price;
else
return cnt * price;
}
virtual void debug() override{
cout << "The book number is " << isbn() << endl;
cout << "Single price is " << price << endl;
cout << "The minimum amount of discount is " << quantity << endl;
cout << "The discount is " << discount << endl;
}
//protected:
// size_t min_qty = 0;
// double discount = 0.0;
};
class Bulk_Quote_Over:public Disc_Quote{
public:
Bulk_Quote_Over() = default;
Bulk_Quote_Over(const string& book, double p, size_t qty, double disc,size_t ov) :
Disc_Quote(book,p,qty,disc),over_amount(ov){}
double net_price(size_t cnt) const override
{
double ret;
if (cnt<quantity)
ret=cnt * price;
else if (cnt >= quantity&&cnt<over_amount)
ret=cnt * (1 - discount)*price;
else if (cnt >= over_amount)
ret=over_amount * (1 - discount)*price + (cnt - over_amount)*price;
return ret;
}
virtual void debug() override {
cout << "The book number is " << isbn() << endl;
cout << "Single price is " << price << endl;
cout << "The minimum amount of discount is " << quantity << endl;
cout << "The maximum amount of discount is " << over_amount << endl;
cout << "The discount is " << discount << endl;
}
private:
size_t over_amount;
};
class noderive final {
noderive() = default;
private:
int i = 0;
};
double print_total(ostream &os, const Quote &item, size_t n)
{
double ret = item.net_price(n);
os << "ISBN:" << item.isbn() << " # sold:" << n << " total due:" << ret << endl;
return ret;
}
print_total(cout,basic,20);
print_total(cout,bulk,20);
\\直到使用时才知道到底是用了哪种参数
在C++中使用基类指针将发生动态绑定。
15.2
这一节我们开始定义基类和派生类。基类以virtual声明虚函数。注意在派生类中继承基类虚函数而又根据自己情况改变的行为称为覆盖override。访问权限增加protected表示派生类和自己可以访问,用户代码无法访问的成员类型。其次注意使用类派生列表,表示该类由某一或多基类继承而得。:public/private B.为了避免继承的虚函数与自身的成员函数发生同名的冲突,使用override表示覆盖而得。指针或应用会发生派生类到基类的类型转换。注意派生类构造函数,首先要保证基类的构造。对于静态变量static无论派生类从基类继承多少对象,只继承独一的静态对象。再注意直接基类和间接基类的问题,上例中
Bulk_Quote_Over的直接基类是Bulk_Disc,间接基类是Bulk。
如果想要杜绝继承的发生,则在类名后加final。
注意无论是智能指针还是内置指针,都支持派生类向基类的转换。不存在基类向派生类的隐式转换,在对象之间不存在转换,如
Quote a;
Bulk_quote b;
b=a;//错误,对象之间不能相互转换
有非常重要的三句话一点要理解:
1.从派生类向基类的类型转换只针对指针或引用类型有效
2.基类向派生类不存在隐式类型转换
3.和任何其他成员一样派生类向基类的转换也可能会由于访问受限而变得不可行。
15.3
OPP的核心思想是多态性(POLYMORPHISM)引用和指针的静态类型和动态类型不同是C++支持多态性的根本所在。直到运行时才确定哪个版本,判断的依据是引用或指针所绑定对象的真实类型。另一方面,对非虚函数的调用在编译时绑定。当且仅当对通过指针或引用调用虚函数时,才会在运行时解析该调用,也只有在这种情况下对象的动态类型才有可能与静态类型不同。
当 派生类继承虚函数时,形参必须一致,返回类型绝大多数情况一致,除非返回类型是自身的指针或调用。
如果虚函数使用默认实参,则基类和派生类默认实参最好一致。
通常情况下,只有成员函数(或友元)中的代码才需使用作用域运算符来回避序参数的机制。如果一个派生类函数需要调用他的基类版本,但没有作用域运算符,则将被理解为对自己的调用,导致无穷递归。
15.4
可以用一种仅表示传递关系而不实际使用的基类。这种基类中有纯虚函数,表示不给基类默认,要求每个派生类自己定义的虚函数。virtual xx()=0.含有纯虚函数的基类就是抽象基类。