继承是在已有类的基础上创建新类的过程,一个B类继承A类,或称从类A派生类B,类A称为基类(父类),类B称为派生类(子类)
注意:
类之间有继承关系,但对象之间没有任何关系,每个对象都是独立的,都有自己的副本。
另外,继承关系并不是一开始就考虑的,一般先考虑功能设计类,再从中提取继承关系,这一点也要注意一下
类继承关系的语法形式
class 派生类名:基类名表
{
数据成员和成员函数声明
};
基类名表 构成
访问控制 基类名1,访问控制 基类名2,……,访问控制 基类名n
访问控制 表示派生类对基类的继承方式,使用关键字:
public 公有继承
private 私有继承
protected 保护继承
//查询功能类
class Find{
protected:
vector<Book> a; //图书向量
multimap<string,int> bookname; //书名索引
multimap<string,int> booknumber; //书号索引
multimap<string,int> author; //作者索引
multimap<string,int> publisher; //出版社索引
multimap<string,int> borrowedrecord_1; //借阅记录书号索引
multimap<string,int> borrowedrecord_2; //借阅记录图书类型索引
multimap<Date,int> publishtime; //出版日期索引
vector<BorrowedRecord> c; //借阅记录向量
public:
Find();
~Find();
bool findBookByNumber(string booknumber); //按书号查找图书
bool findBookByName(string bookname); //按书名查找图书
bool findBookByPublisher(string publisher); //按出版社查找图书
bool findBookByPublishtime(Date publishtime); //按出版日期查找图书
void findBookByNameHazily(string keyword); //按书名模糊查找图书
bool match(multimap<string,int>::iterator it,string s1); //匹配关键字
bool findBookByCombination_1(string bookname,string author); //组合查找(书名+作者)
bool findBookByCombination_2(string publisher,string author); //组合查找(出版社+作者)
bool findBookByTimeIntervals(Date date_1,Date date_2); //查询指定时间段出版的图书
void findRecordByNumber(string booknumber); //按书号查询图书借阅情况
void findMyselfRecordByBookType(string booktype); //图书类型查询本人的借阅情况
};
//操作类
class Operation:public Find{
Reader b; //读者对象
Date today; //当期日期
public:
Operation(Reader &b,Date today);
~Operation();
void ReaderSave(); //保存读者
void BorrowedRecordSave(); //保存借阅记录
void BorrowedRecordOpen(); //读取借阅记录
void BookSave(); //图书的写操作
void BookOpen(); //图书的读操作
void addBook(Book &b); //添加图书信息
void borrowBook(string booknumber); //借书操作
void returnBook(string booknumber); //还书操作
void renewBook(string booknumber); //续借操作
};
操作类继承了查询功能类,查询功能类为基类(父类),操作类为派生类(子类)
访问控制里有一点需要注意,不论以何种方式继承基类,派生类都不能直接使用基类的私有成员
派生类的生成过程经历了三个步骤:
吸收基类成员(全部吸收(构造、析构除外),但不一定可见)
改造基类成员
添加派生类新成员
吸收基类成员很好理解,就是派生类里除了基类的构造、析构函数它没有,剩下的全部成员它都有。但是有一个地方需要注意,定义派生类对象时只是复制了基类对象的空间,复制过来的数据成员并没有被赋初值
改造基类成员是用过在派生类中定义同名成员(包括成员函数和数据成员)来屏蔽(隐藏)在派生类中不起作用的部分基类成员
也就是说可以通过成员函数的覆盖来改造基类成员
这里说一下函数覆盖和函数重载的区别,覆盖:函数名称、参数什么的都一样,而重载函数只是同名,其他的都可以不一样。
添加新成员是因为是因为仅仅继承基类的成员是不够的,需要在派生类中添加新成员,以保证派生类自身特殊属性和行为的实现
一个小实例
#include <iostream>
usingnamespace std ;
class A{
public:
int a1, a2 ;
A( int i1=0, int i2=0){
a1 =i1;
a2= i2;
}
void print(){
cout<< "a1="<< a1 << '\t' <<"a2="<<a2 << endl;
}
};
class B : public A{
public:
int b1, b2 ;
B( int j1=1, int j2=1){
b1=j1;
b2=j2;
}
void print(){ //定义同名函数
cout<< "b1="<< b1 << '\t'<< "b2="<< b2 << endl ;
}
void printAB(){
A::print(); //派生类对象调用基类版本同名成员函数
print(); //派生类对象调用自身的成员函数
}
};
int main(){
B b;
b.A::print();
b.printAB();
}
派生类中访问静态成员,用以下形式显式说明:
类名::成员
或通过对象访问 对象名.成员
对于构造函数的执行顺序:基类->对象成员->派生类
而析构函数的执行顺序则与构造函数相反
多继承
一个类有多个直接基类的继承关系称为多继承
语法
class 派生类名:访问控制 基类名1,访问控制 基类名2,……,访问控制 基类名n
{
数据成员和成员函数声明
};
类C可以根据访问控制同时继承类A和类B的成员,并添加自己的成员
举个例子
class Base1
{ public:
Base1(int x){ value=x;}
int getData() const { return value ;}
protected:
int value;
};
class Base2
{ public:
Base2(char c) { letter=c;}
char getData() const { return letter;}
protected:
char letter;
class Derived : public Base1, public Base2
{
public :
Derived ( int,char,double );
double getReal() const ;
private :
double real ;
};
int main()
{ Base1 b1(10);
Base2 b2('k');
Derived d(5,'A',2.5);
return;
}
Derived类同时继承了 Base1、Base2两个类(多继承)
赋值兼容规则
赋值兼容规则指在程序中需要使用基类对象的任何地方,都可以用公有派生类的对象来替代。
赋值兼容规则中所指的替代包括以下的情况:
a 派生类的对象可以赋给基类对象
b 派生类的对象可以初始化基类的引用
c 派生类的对象的地址可以赋给基类类型的指针
也可以把指向派生类对象的指针赋值给指向基类对象的指针
在替代之后,派生类对象就可以作为基类的对象使用,但只能使用从基类继承的成员,这是赋值兼容规则的特点
另外基类想操作派生类中的成员也不是没有办法,可进行强制类型转换,将其转化成指向派生类的指针进行操作
赋值兼容规则应注意的问题
(1)声明为指向基类的指针可以指向它的公有派生类的对象,但不允许指向它的私有派生类的对象。
(2)允许将一个声明为指向基类的指针指向其公有派生类对象,但是不能将一个声明为指向派生类对象的指针指向其基类的一个对象。
(3)声明为指向基类对象的指针,当其指向公有派生类对象时,只能用它来直接访问派生类中从基类继承来的成员,而不能直接访问公有派生类的定义的成员。
虚函数与多态性
多态性是指一个名字,多种语义;或界面相同,多种实现
重载函数是多态性的一种简单形式
虚函数允许函数调用与函数体的联系在运行时才进行,称为动态联编
根据赋值兼容,用基类类型的指针指向派生类,就可
以通过这个指针来使用类(基类或派生类)的成员函
数。
如果这个函数是普通的成员函数,通过基类类型的指
针访问到的只能是基类的同名成员。
而如果将它设置为虚函数,则可以使用基类类型的指
针访问到指针正在指向的派生类的同名函数。从而
实现运行过程的多态。
根据赋值兼容,用基类类型的指针指向派生类,就可以通过这个指针来使用类(基类或派生类)的成员函数。如果这个函数是普通的成员函数,通过基类类型的指针访问到的只能是基类的同名成员。而如果将它设置为虚函数,则可以使用基类类型的指针访问到指针正在指向的派生类的同名函数。从而实现运行过程的多态。
多态的实现必须具备的两个条件:1.有继承、覆盖关系2.必须要用基类对象(指针)来展现派生类的各个功能。不能直接用派生类来调用相关功能,这样无法实现多态
实现动态联编方式的前提:
先要声明虚函数
类之间满足赋值兼容规则
通过指针与引用来调用虚函数
冠以关键字virtual的成员函数称为虚函数
实现运行时多态的关键首先是要说明虚函数,另外,必须用基类指针调用派生类的不同实现版本
注意:
一个虚函数,在派生类层界面相同的重载函数都保持虚特性
虚函数必须是类的成员函数
不能将友元说明为虚函数,但虚函数可以是另- -个类的友元
析构函数可以是虚函数,但构造函数不能是虚函数
另外几个需要注意的地方:
1.派生类应该从它的基类公有派生。
2.必须首先在基类中定义虚函数。
3.派生类对基类中声明虚函数重新定义时,关键字virtual可以不写。
4.一般通过基类指针访问虚函数时才能体现多态性。
5.一个虚函数无论被继承多少次,保持其虚函数特性。
6.虚函数必须是其所在类的成员函数,而不能是友元函数,也不能是静态函数。
7.构造函数、内联成员函数、静态成员函数不能是虛函数。
(虛函数不能以内联的方式进行处理)
8.析构函数可以是虛函数,通常声明为虚函数。
纯虚函数是一种特殊的虚函数,在许多情况下,在基类中不能对虚函数给出有意义的实现,而把它声明为纯虚函数,它的实现留给该基类的派生类去做。这就是纯虚函数的作用。
纯虚函数是一个在基类中说明的虚函数,在基类中没有定义,要求任何派生类都定义自己的版本
纯虚函数为各派生类提供一个公共界面
纯虚函数说明形式:
virtual 类型 函数名(参数表)=0
一个具有纯虚函数的基类称为抽象类
虚函数与多态的应用
虚函数和多态性使成员函数根据调用对象的类型产生不同的动作
多态性特别适合于实现分层结构的软件系统,便于对问题抽象时定义共性,实现时定义区别
定义完抽象类不能直接生成对象,但可以定义指针和引用的对象
抽象类存在以后,派生类必须要重写纯虚函数,否则会出错
反思:最近心有点野了,玩的时间比较长,有时候代码搞得都太匆忙,感觉有点松懈了,还没到考试,就不能玩的太过,有时间要把知识点和写过得代码好好看一看。好好调整一下自己的状态,为下阶段的课程设计再加把劲!!!