1.OPP:概述
面向对象程序设计的核心思想是数据抽象、继承和动态绑定。
通过继承联系在一起的类构成一种层次关系,在层次关系的根部的是基类,基类下面的类是派生类
基类负责定义在层次关系中所有类共同拥有的成员,而每个派生类定义各自特有的成员。
虚函数:由基类来声明,但基类希望它的派生类各自重新定义适合自身的版本的函数。
class quote{
public:
virtual double net_price(std::size_t n) const; //基类quote声明的虚函数
};
派生类必须通过使用类派生列表明确指出它是从哪个基类继承而来。
class bulk_quote : public quote{ //在基类前写上访问说明符
...
};
派生类必须在其内部对所有重新定义的虚函数进行声明。
新标准允许派生类使用关键字override来显式地注明它将使用哪个成员函数来改写基类的虚函数。
double net_price(std::size_t) const override; //派生类重新定义的虚函数
通过动态绑定,我们能用同一段代码分别处理基类和派生类的对象。
在C++语言中,当我们使用基类的引用或指针调用一个虚函数时将发生动态绑定。
double print_total(ostream& os,const quote& item, size_t n)
{
auto ret = item.net_price();
}
//basic是基类类型,bulk是派生类类型
print_total(cout,basic,20); //调用基类的net_price
print_total(cout,bulk,20); //调用派生类的net_price
函数的执行版本由实参决定,在运行时选择函数的版本,动态绑定又称为运行时绑定。
2.定义基类和派生类
2.1、定义基类
基类通常都应该定义一个虚析构函数,即使该函数不执行任何实际操作也是如此。
派生类可以继承其基类的成员。
在C++中,基类必须将它的两种成员函数区分开来:一种是希望派生类进行覆盖的函数,另一种是希望派生类直接继承而不要改变的函数。
基类通过在其成员函数语句前加上关键字virtual,使得该函数执行动态绑定,成为虚函数。
任何构造函数之外的非静态函数都可以是虚函数。
关键字virtual只能出现在类内部的声明语句之前而不能用于类外部的函数定义。
若基类把一个函数声明成虚函数,则该函数在派生类中隐式地也是虚函数。
成员函数如果没被声明为虚函数,则其解析过程发生在编译时而非运行时。
派生类能访问基类的公有成员,而不能访问基类的私有成员。
若基类希望它的派生类有权访问该成员,同时禁止其他用户访问,可使用protected访问运算符来说明该成员。
class quote{ //基类
public:
quote() = default; //默认构造函数
quote(const std::string& book,double sales_price) :
books(book),price(sales_price) {} //接受两个参数的构造函数
virtual double net_price(std::size_t n) const {return n*price;} //虚函数
virtual ~quote() = default; //虚析构函数
private:
std::string books;
protected: //派生类能够访问的成员
double price = 0.0;
};
2.2、定义派生类
派生类在每个基类前面可以有三种访问说明符中的一种:public、protected、private。
访问说明符的作用是控制派生类从基类继承而来的成员是否对派生类用户可见。
若一个派生是公有的,则基类的公有成员也是派生类接口的组成部分。(在任何需要基类的引用或指针的地方我们都可以使用派生类的对象)
若派生类没有覆盖基类中的某个虚函数,则派生类会直接继承其在基类中的版本。
新标准允许派生类使用关键字override显式地注明它使用某个成员函数覆盖了它所继承的虚函数。
一个派生类对象包含多个组成部分:一个含有派生类自己定义的非静态成员的子对象,一个与该派生类继承的基类对应的子对象。
由于在派生类对象中含有与其基类对应的组成部分,所以能把派生类的对象当作基类对象来使用,也能将基类的指针或引用绑定到派生类对象中的基类部分。
这种转换称为派生类到基类的类型转换,编译器会隐式地执行派生类到基类的转换。
在派生类对象中含有与其基类对应的组成部分,这一事实是继承的关键所在。
派生类只能通过使用基类的构造函数来初始化它的基类部分,每个类控制他自己的成员的初始化过程。
派生类构造函数是通过构造函数初始化列表来将实参传递给基类构造函数的。
首先初始化基类的部分,然后按照声明的顺序依次初始化派生类的成员。
bulk_quote(const std::string& book,double p,std::size_t qty,double disc) :
quote(book,p),qtys(qty),discount(disc) {}
//将实参book,p传给quote的构造函数,剩余的实参依次初始化派生类的成员
派生类的作用域嵌套在基类的作用域之内,派生类可以直接访问基类的公有成员和受保护成员。
若基类定义了一个静态成员,则在整个继承体系中只存在该成员的唯一定义。
静态成员遵循通用的访问控制规则,若基类中的成员是private的,则派生类无权访问该成员。