目录
什么是继承和派生
单继承:一个派生类只从一个基类派生
多重继承:一个派生类有两个或多个基类
派生是基类的具体化,而基类则是派生类的抽象
派生类的声明方式
声明派生类的一般形式:
class 派生类名:[继承方式] 基类名
{
派生类新城的成员
};
如果不写继承方式,则为private
派生类的构成
1、从基类接收成员:派生类把基类全部的成员(不包括构造函数和析构函数)接收过来,也即是说没有选择的
2、调整从基类接收的成员:成员人员可以对接收的成员做某些调整
举例1:通过继承把基类的公用成员指定为派生类的访问性为私有(派生类外不能访问)
举例2:派生类声明一个与基类成员同名的成员,则派生类的新成员会覆盖基类的同名函数
举例3:如果为成员函数,则应该函数名和函数参数表(参数个数和类型)都相同,为覆盖,如果不相同,为重载
3、声明派生类时增加的成员
声明派生类时,需要自己定义派生类的构造函数和析构函数,因为构造函数和析构函数不能从基类继承
派生类成员的访问属性
1、公有继承:基类的公有成员和保护成员在派生类中保持原有的访问属性,其私有成员仍为基类私有
2、私有继承:基类的公有成员和保护成员在派生类中为私有成员,其私有成员仍为基类私有
3、受保护的继承:基类的公有成员和保护成员在派生类中为保护成员,其私有成员为基类私有
派生类的构造函数和析构函数
简单的派生类的构造函数
在执行派生类构造函数时,先调用基类的构造函数,再调用派生类的构造函数。
在执行派生类析构函数时,先执行派生类的析构函数,再调用基类的析构函数。
派生类构造函数名(总参数表): 基类构造函数名(参数表)
{
派生类新增数据成员初始化语句;
}
//方法一 Student1::Student1(int n,string na,char s,int a,string ad):Student(n,na,s) { age = a; addr = ad; } //方法二 Student1::Student1(int n,string na,char s,int a,string ad):Student(n,na,s),age(a),addr(ad){} //调用方式 class Student1 student1(1001,"andrew",'m',25,"石景山区");
若在基类构造函数中添加规定参数,则
//定义方式 Student1::Student1(string na,char s,int a,string ad):Student(1001,na,s),age(a),addr(ad){} //调用方式 class Student1 student1("andrew",'m',25,"石景山区");
有子对象派生类的构造函数
在派生类的数据成员中,有基类的对象
派生类构造函数的任务包含了三部分:
1)对基类数据成员的初始化
2)对子对象数据成员初始化
3)对派生类数据成员初始化
执行派生类构造函数的顺序:
1)调用基类构造函数,对基类数据成员初始化
2)调用子对象构造函数,对子对象数据成员初始化
3)执行派生类构造函数本身,对派生类数据成员初始化
定义派生类构造函数的一般形式:
派生类构造函数名(总参数表):基类构造函数名(参数表),子对象名(参数表)
{
派生类中新增数据成员初始化语句
}
//类声明文件
#include<iostream>
using namespace std;
class Student
{
public:
Student(int,string,char);
~Student();
void displayStu();
protected:
int num;
string name;
char sex;
};
class Student1:public Student
{
public:
Student1(int ,string,char,int ,string,char,int,string);
void display();
private:
Student monitor;
int age;
string addr;
};
//类实现文件
#include<iostream>
#include <string>
#include "student.h"
using namespace std;
Student::Student(int n,string na,char s):num(n),name(na),sex(s){}
Student::~Student(){}
void Student::displayStu()
{
cout<<"num:"<<num<<endl;
cout<<"name:"<<name<<endl;
cout<<"sex:"<<sex<<endl;
}
Student1::Student1(int n,string na,char s,int n1,string na1,char s1,int a,string ad):Student(n,na,s),monitor(n1,na1,s1),age(a),addr(ad){}
void Student1::display()
{
cout<<"the information of Student:"<<endl;
cout<<"num:"<<num<<endl;
cout<<"name:"<<name<<endl;
cout<<"sex:"<<sex<<endl;
cout<<"age:"<<age<<endl;
cout<<"addr:"<<addr<<endl;
cout<<"the information of monitor:"<<endl;
monitor.displayStu();
}
//主函数文件
class Student1 student1(10025,"andrew",'m',10000,"G.E.M.",'f',25,"石景山区");
student1.display();
多层派生时的构造函数
在公有继承时,尽管基类的私有数据成员,派生类无法访问,但是可以通过构造函数实现初始化,在基类中用公有函数调用,然后派生类调用基类的公有函数。
构造函数:
Student2::Student2(int n,string na,int a,int s):Student1(n,na,a),score(s){};
//类声明文件
#include<iostream>
using namespace std;
class Student
{
public:
Student(int,string);
void displayStu();
protected:
int num;
string name;
};
class Student1:public Student
{
public:
Student1(int,string,int );
void displayStu1();
private:
int age;
};
class Student2:public Student1
{
public:
Student2(int,string,int,int);
void displayStu2();
private:
int score;
};
//类实现文件
#include<iostream>
#include <string>
#include "student.h"
using namespace std;
/*int num;
string name*/;
Student::Student(int n,string na):num(n),name(na){}
void Student::displayStu()
{
cout<<"num:"<<num<<endl;
cout<<"name:"<<name<<endl;
}
//int age;
Student1::Student1(int n,string na,int a):Student(n,na),age(a){}
void Student1::displayStu1()
{
Student::displayStu();
cout<<"age:"<<age<<endl;
}
//int score;
Student2::Student2(int n,string na,int a,int s):Student1(n,na,a),score(s){};
void Student2::displayStu2()
{
Student1::displayStu1();
cout<<"score:"<<score<<endl;
}
//主函数文件
int num = 10025;
string name = "andrew";
int age = 25;
int score = 99;;
class Student2 student2(num,name,age,score);
student2.displayStu2();
先初始化基类的数据成员:num和name;
在初始化Student1的数据成员:age;
最后再初始化Student2的数据成员:score;
派生类构造函数的特殊形式
1、当不需要对派生类新增的成员进行任务初始操作时,派生类构造函数的函数体可以为空,即构造函数是空函数
派生类构造函数没有新增成员:
派生类构造函数的参数 = 基类构造参数 + 子对象参数
派生类构造函数的作用:只是为了将参数传递给基类构造函数和子对象,同时在执行派生类构造函数时调用基类构造函数和子对象构造函数
2、如果在基类中没有定义构造函数,或定义了没有参数的构造函数,那么,在定义派生类构造函数时可以不写基类构造函数
基类构造函数没有参数:
如果在基类和子对象类型的声明都没有定义带参数的构造函数,而且也不需对派生类自己的数据成员初始化,则不必显式的定义派生类构造函数。派生类构造函数没有传递参数的任务,也没有对数据成员初始化的任务。在建立派生类对象时,系统会自动调用派生类的默认构造函数,在调用派生类构造函数时,调用基类的默认构造函数和子对象类型的默认构造函数。
如果在基类和子对象类型的声明都定义了带参数的构造函数,那么就必须显式地定义派生类构造函数,并在派生类构造函数中写出基类或子对象类型的构造函数及其参数表。
如果在基类中即定义了无参数的构造函数和有参数的构造函数(构造函数的重载),则在定义派生类构造函数时,既可以包含基类构造函数及其参数,也可以不包含基类构造函数。在调用派生类构造函数时,根据构造函数内容决定调用基类的有参的构造函数还是无参的构造函数。
派生类的析构函数
调用顺序和构造函数正好相反:
先调用派生类构造函数,对派生类新增的成员进行清理
再调用子对象的析构函数,对子对象进行清理
最后调用基类的析构函数,对基类进行清理
多重继承
声明多重继承的方法
class D:public A,private B,protected C
{
类D新增的成员
}
定义多重继承派生类的构造函数
派生类构造函数名(总参数表):基类1构造函数(参数表),基类2构造函数(参数表),基类3构造函数(参数表)
{
派生类中新增数据成员初始化语句
}
//类声明
#include<iostream>
#include<string>
using namespace std;
class Teacher
{
public:
Teacher(string ,int ,string);
void displayTeach();
protected:
string name;
int age;
string title;
};
class Student
{
public:
Student(string, char ,float);
void displayStu();
protected:
string name1;
char sex;
float score;
};
class Graduate:public Teacher,public Student
{
public:
Graduate(string ,int ,string,char,float,float);
void displayGrad();
private:
float wage;
};
//类实现
#include "worker.h"
Teacher::Teacher(string na ,int a,string ti):name(na),age(a),title(ti){}
void Teacher::displayTeach()
{
cout<<"name:"<<name<<endl;
cout<<"age:"<<age<<endl;
cout<<"title:"<<title<<endl;
}
Student::Student(string na1, char s,float sc):name1(na1),sex(s),score(sc){}
void Student::displayStu()
{
cout<<"name:"<<name1<<endl;
cout<<"sex:"<<sex<<endl;
cout<<"score:"<<score<<endl;
}
Graduate::Graduate(string na ,int a,string ti,char s,float sc,float w):Teacher(na,a,ti),Student(na,s,sc),wage(w){}
void Graduate::displayGrad()
{
cout<<"name:"<<name<<endl;
cout<<"age:"<<age<<endl;
cout<<"title:"<<title<<endl;
cout<<"sex:"<<sex<<endl;
cout<<"score:"<<score<<endl;
cout<<"wage:"<<wage<<endl;
}
//主函数文件
string name = "andrew";
int age = 25;
string title = "深度学习";
char sex = 'm';
float score = 98.5;
float wage = 100000;
class Graduate graduate(name ,age ,title ,sex ,score ,wage);
多重继承引起的二义性问题
1)、两个基类有同名成员,避免二义性可以添加 类名::
2)、两个基类和派生类三者都有同名成员
基类的同名成员在派生类中被屏蔽,成为“不可见”的,或者说,派生类新增加的同名成员覆盖了基类中的同名成员。
不同的成员函数,只有在函数名和参数个数相同,类型相匹配的情况下才发生同名覆盖,如果只有函数名相同而参数不容, 不会发生同名覆盖,而属于函数重载。
虚基类
1、虚基类的作用:
虚基类:使得在继承间接共同基类时只保留一份成员
class A
{...};
class B:virtual public A
{...};
class C:virtual public A
{...};
注意:
虚基类并不是在声明基类是声明的,而是在声明派生类时,指定继承方式时声明的
为了保证虚基类在派生类中只继承一次,应当在该基类的所有派生类中声明为虚基类。否则仍会出现对基类多次继承
2、虚基类的初始化:
class A
{
A(int i){}
...
};
class B:virtual public A
{
B(int n):A(n){}
...
};
class C:virtual public A
{
C(int n):A(n){}
...
};
class D:public B,public C
{
D(int n):A(n),B(n),C(n){}
...
};
规定:最后的派生类不仅要负责对其直接基类进行初始化,还要负责对虚基类初始化
//类声明文件
#include<iostream>
#include<string>
using namespace std;
class Person
{
public:
Person(string ,char ,int);
void display();
protected:
string name;
char sex;
int age;
};
class Teacher:virtual public Person
{
public:
Teacher(string ,char ,int ,string);
void display();
protected:
string title;
};
class Student:virtual public Person
{
public:
Student(string ,char ,int ,float);
void display();
protected:
float score;
};
class Graduate:public Teacher,public Student
{
public:
Graduate(string ,char ,int ,string ,float ,float);
void display();
private:
float wage;
};
//类实现文件
#include "people.h"
using namespace std;
/*string name;
char sex;
int sex;
string title;
float score;
float wage;*/
Person::Person(string na,char s,int a):name(na),sex(s),age(a){};
Teacher::Teacher(string na,char s,int a,string ti):Person(na,s,a),title(ti){};
Student::Student(string na,char s,int a,float sco):Person(na,s,a),score(sco){};
Graduate::Graduate(string na,char s,int a,string ti,float sco,float w):Person(na,s,a),Teacher(na,s,a,ti),Student(na,s,a,sco),wage(w){};
void Person::display()
{
cout<<"name:"<<name<<endl;
cout<<"sex:"<<sex<<endl;
cout<<"age:"<<age<<endl;
}
void Teacher::display()
{
cout<<"name:"<<name<<endl;
cout<<"sex:"<<sex<<endl;
cout<<"age:"<<age<<endl;
cout<<"title:"<<title<<endl;
}
void Student::display()
{
cout<<"name:"<<name<<endl;
cout<<"sex:"<<sex<<endl;
cout<<"age:"<<age<<endl;
cout<<"score:"<<score<<endl;
}
void Graduate::display()
{
cout<<"name:"<<name<<endl;
cout<<"sex:"<<sex<<endl;
cout<<"age:"<<age<<endl;
cout<<"title:"<<title<<endl;
cout<<"score:"<<score<<endl;
cout<<"wage:"<<wage<<endl;
}
//主函数文件
#include <iostream>
#include<string>
#include"people.h"
using namespace std;
int main(){
string name = "andrew";
char sex = 'm';
int age = 25;
string title = "机器学习";
float score = 98.5;
float wage = 100000;
class Graduate graduate(name,sex,age,title,score,wage);
graduate.display();
system("pause");
return 0;
}
注意:如果单一继承解决的问题不要使用多重继承
基类和派生类的转换
公有派生类具有基类的全部功能,所以基类能够实现的功能,公有派生类都能实现。
只有公有派生类才是基类真正的子类型,他完整地继承了基类的功能
赋值兼容:不同类型数据之间的自动转换和赋值
1、派生类对象可以向基类对象赋值
class A a1;
class B b1;
a1 = b1;
上述代码属于“大材小用”。
赋值仅仅是对数据成员的赋值,对函数成员不存在赋值的问题
只能用子类对象对基类对象赋值,而不能基类对象对子类对象赋值
同一基类的不同派生类对象之间也不能赋值
2、派生类对象可以替代基类对象向基类对象的引用进行赋值或初始化
class A a1;
class B b1;
class A& r = b1;
important:此时r并不是b1的别名,也不和b1共享同一段存储单元。
此时r是b1中基类部分的别名,r和b1中基类部分共享同一段存储单元,r和b1具有相同的起始地址
3、如果函数的参数是基类对象或基类对象的引用,相应的实参可以用子类对象
void fun(A& r){cout<<r.num;}
fun(b1);
4、派生类对象的地址可以赋值给指向基类对象的指针变量,也就是说,指向基类的指针变量也可以用来指向派生类对象
class A*ptr;
class B b1;
ptr = &b1;
//类声明文件
#include<iostream>
#include<string>
using namespace std;
class Student
{
public:
Student(int ,string ,float);
void display();
private:
int num;
string name;
float score;
};
class Graduate:public Student
{
public:
Graduate(int ,string ,float ,float);
void display();
private:
float wage;
};
//类实现文件
#include<iostream>
#include<string>
#include"person.h"
using namespace std;
/* int num;
string name;
float score;
float wage;*/
Student::Student(int n,string na,float sc):num(n),name(na),score(sc){}
Graduate::Graduate(int n,string na,float sc,float w):Student(n ,na ,sc),wage(w){}
void Student::display()
{
cout<<"num:"<<num<<endl;
cout<<"name:"<<name<<endl;
cout<<"score:"<<score<<endl;
}
void Graduate::display()
{
Student::display();
cout<<"wage:"<<wage<<endl;
}
//主函数文件
int num = 1001;
string name = "andrew";
float score = 98.5;
float wage = 100000;
class Student student(num,name,score);
class Graduate graduate(num,name,score,wage);
graduate.display();
cout<<"--------------------------------"<<endl;
class Student student1 = graduate;
student1.display();
cout<<"--------------------------------"<<endl;
class Student *student2 = &graduate;
student2->display();
继承与组合
在一个类中以另一个类的对象作为数据成员的,称为类的组合
继承是纵向的,组合是横向的