# 概念
* 在C++中可重用性是通过继承(inheritance)这一机制来实现的。
* “继承”就是在一个已存在的类的基础上建立一个新的类
* 一个新类从已有的类那里获得其已有特性,这种现象称为类的继承。
* 通过继承,派生类继承了基类的所有数据成员和绝大多数成员函数(构造函数和析构函数除外),并可以对成员作必要的增加或调整。
* 一个基类可以派生出多个派生类,每一个派生类又可以作为基类再派生出新的派生类,因此基类和派生类是相对而言的。
* 一个派生类有两个或多个基类的称为多重继承(multiple inheritance)。
* 派生类是基类的具体化,而基类则是派生类的抽象。
# **派生**
## **声明方式**
~~~
class 派生类名: [继承方式] 基类名
{
派生类新增加的成员
} ;
~~~
### **继承方式**
* 继承方式包括: public(公用的),private(私有的)和protected(受保护的)
* 如果不写此项,则默认为private(私有的)。
![image-20200607004332556](file://C:/Users/28165/AppData/Roaming/Typora/typora-user-images/image-20200607004332556.png?lastModify=1591534314)
## **构成**
* 基类中继承来的数据成员和成员函数
* 新增的数据成员和成员函数
* 构造函数和析构函数不能继承,所以需要自己重新写
## 构造函数
* 派生类所增加的数据成员的初始化
* 基类的数据成员初始化
* 使用冒号语法,对基类数据成员进行初始化
### 顺序
① 首先,调用基类构造函数,对基类数据成员初始化;
② 其次,调用子对象构造函数,对对象成员初始化;
③ 最后,执行派生类构造函数本身,对派生类普通数据成员初始化.
### 形式
~~~
派生类构造函数名(总参数表列): 基类构造函数名(参数表列),对象成员名(参数表列)
{
//派生类中新增数据成员的初始化语句
}
~~~
### 例子
~~~
#include <iostream>
#include<string>
using namespace std;
class Student //声明基类Student
{
public:
Student(int n,string nam,char s) //基类构造函数
{
num=n;
name=nam;
sex=s;
}
~Student( ){ } //基类析构函数
protected: //保护部分
int num;
string name;
char sex ;
};
class Student1: public Student //声明派生类Student1
{
public: //派生类的公用部分
Student1(int n,string nam,char s,int a,string ad):Student(n,nam,s) //派生类构造函数
{
age=a; //在函数体中只对派生类新增的数据成员初始化
addr=ad;
}
void show( )
{
cout<<″num: ″<<num<<endl;
cout<<″name: ″<<name<<endl;
cout<<″sex: ″<<sex<<endl;
cout<<″age: ″<<age<<endl;
cout<<″address: ″<<addr<<endl<<endl;
}
~Student1( ){ } //派生类析构函数
private: //派生类的私有部分
int age;
string addr;
};
int main( )
{
Student1 stud1(10010,″Wang-li″,′f′,19,″115 Beijing Road,Shanghai″);
Student1 stud2(10011,″Zhang-fun″,′m′,21,″213 Shanghai Road,Beijing″);
stud1.show( ); //输出第一个学生的数据
stud2.show( ); //输出第二个学生的数据
return 0;
}
~~~
# 多重继承
一个派生类同时继承多个基类,称为多重继承。
## 形式
~~~
class D:public A,private B,protected C
{
//类D新增加的成员
}
//构造函数
派生类构造函数名(总参数表列): 基类1构造函数(参数表列), 基类2构造函数(参数表列), 基类3构造函数 (参数表列)
{
//派生类中新增数成员据成员初始化语句
}
~~~
## 例子
~~~
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
class Teacher //声明类Teacher(教师)
{
public: //公用部分
Teacher(string nam,int a, string t) //构造函数
{
name=nam;
age=a;
title=t;
}
void display( ) //输出教师有关数据
{
cout<<″name:″<<name<<endl;
cout<<″age″<<age<<endl;
cout<<″title:″<<title<<endl;
}
protected: //保护部分
string name;
int age;
string title; //职称
};
class Student //定义类Student(学生)
{
public:
Student(char nam[],char s,float sco)
{
strcpy(name1,nam);
sex=s;
score=sco;
} //构造函数
void display1( ) //输出学生有关数据
{
cout<<″name:″<<name1<<endl;
cout<<″sex:″<<sex<<endl;
cout<<″score:″<<score<<endl;
}
protected: //保护部分
string name1;
char sex;
float score; //成绩
};
class Graduate:public Teacher,public Student //声明多重继承的派生类Graduate
{
public:
Graduate(string nam,int a,char s, string t,float sco,float w)
:Teacher(nam,a,t),Student(nam,s,sco),wage(w) { }
void show( ) //输出研究生的有关数据
{
cout<<″name:″<<name<<endl;
cout<<″age:″<<age<<endl;
cout<<″sex:″<<sex<<endl;
cout<<″score:″<<score<<endl;
cout<<″title:″<<title<<endl;
cout<<″wages:″<<wage<<endl;
}
private:
float wage; //工资
};
int main( )
{
Graduate grad1(″Wang-li″,24,′f′,″assistant″,89.5,1234.5);
grad1.show( );
return 0;
}
~~~
## 二义性
当基类与派生类之间出现相同的数据成员时,就有二义性
### 解决办法
~~~
// 指明作用域
//Student和Teacher类中有相同的name数据时
cout<<″name:″<<Teacher::name<<endl; //在Student类中调用Teacher类的name
~~~
### 同名覆盖
* 基类的同名成员在派生类中被屏蔽,成为“不可见”的
* 派生类新增加的同名成员覆盖了基类中的同名成员。
* 不同的成员函数,只有在函数名和参数个数相同、类型相匹配的情况下才发生同名覆盖,如果只有函数名相同而参数不同,不会发生同名覆盖,而属于函数重载。
# 虚基类
* 使得在继承间接共同基类时只保留一份成员。
* 虚基类并不是在声明基类时声明的,而是在声明派生类时,指定继承方式时声明的
* 在最后的派生类中不仅要负责对其直接基类进行初始化,还要负责对虚基类初始化。
* C++编译系统只执行最后的派生类对虚基类的构造函数的调用,而忽略虚基类的其他派生类(如类B和类C) 对虚基类的构造函数的调用,这就保证了虚基类的数据成员不会被多次初始化。
## 形式
~~~
class A //声明基类A
{…};
class B :virtual public A //声明类B是类A的公用派生类,A是B的虚基类
{…};
class C :virtual public A //声明类C是类A的公用派生类,A是C的虚基类
{…};
~~~
## 例子
~~~
#include <iostream>
#include <string>
using namespace std;
//声明公共基类Person
class Person
{
public:
Person(string nam,char s,int a)//构造函数
{
name=nam;sex=s;age=a;
}
protected: //保护成员
string name;
char sex;
int age;
};
//声明Person的直接派生类Teacher
class Teacher:virtual public Person //声明Person为公用继承的虚基类
{
public:
Teacher(string nam,char s,int a, string t)
:Person(nam,s,a) //构造函数
{
title=t;
}
protected: //保护成员
string title; //职称
};
//声明Person的直接派生类Student
class Student:virtual public Person //声明Person为公用继承的虚基类
{
public:
Student(string nam,char s,int a,float sco) //构造函数
:Person(nam,s,a),score(sco){ } //初始化表
protected: //保护成员
float score; //成绩
};
//声明多重继承的派生类Graduate
class Graduate:public Teacher,public Student //Teacher和Student为直接基类
{
public:
Graduate(string nam,char s,int a, string t,float sco,float w) //构造函数
:Person(nam,s,a),Teacher(nam,s,a,t),Student(nam,s,a,sco),wage(w){}//初始化表
void show( ) //输出研究生的有关数据
{
cout<<″name:″<<name<<endl;
cout<<″age:″<<age<<endl;
cout<<″sex:″<<sex<<endl;
cout<<″score:″<<score<<endl;
cout<<″title:″<<title<<endl;
cout<<″wages:″<<wage<<endl;
}
private:
float wage; //工资
};
int main( ) //主函数
{
Graduate grad1(″Wang-li″,′f′,24,″assistant″,89.5,1234.5);
grad1.show( );
return 0;
}
~~~
# 继承与组合
* 在一个类中以某类对象作为数据成员的,称为类的组合(composition)。
* 继承是纵向的,组合是横向的。
- 介绍
- 编程设计语言
- 第一章 对C++的初步认识
- 1.2 最简单的C++程序
- 1.3 C++对C的补充
- 1.3.1 return
- 1.3.2 输入输出流
- 1.3.3 putchar 和 getchar
- 1.3.4 用const定义常变量
- 1.3.5 函数原型声明
- 1.3.6 内置函数
- 1.3.7 函数重载
- 1.3.8 函数模板
- 1.3.9 有默认值的参数
- 1.3.10 作用域
- 1.3.11 const修饰指针
- 1.3.12 引用
- 1.3.13 生命期
- 1.3.14 变量
- 1.3.15 字符串变量
- 第二章 类与对象
- 2.2 类的声明和对象的定义
- 2.3 类的成员函数
- 第三章 关于类和对象的进一步讨论
- 3.1 构造函数
- 3.1.1 对象的初始化
- 3.1.2 构造函数
- 3.2 析构函数
- 3.3调用析构函数和构造函数的顺序
- 3.4 对象数组
- 3.5 对象指针
- 3.6 共享数据的保护
- 3.7 对象的建立与释放
- 3.8 对象的赋值与复制
- 3.9 静态成员
- 3.10 友元
- 3.11 类模板
- 第四章 运算符重载
- 数据类型转换
- 运算符重载
- 重载流插入运算符和流提取运算符
- 第五章 继承与派生
- 继承与派生
- 第六章 多态性与虚函数
- 多态性
- 虚函数
- 纯虚函数与抽象类