继承与派生
一、基本概念
1、概念
继承和派生是一个东西,在定义一个新的类B时,若该类与已有的类A相似(即B拥有A的全部特点),则可把A作为基类(也称父类),B作为基类的一个派生类(也称子类)。派生类拥有基类全部的成员函数和成员变量,但不能访问基类中的private成员。
2、派生类写法
class 派生类名:基类名
{
};
举个例子:
class people
{
private:
string name;
int age;
public:
void get()
{
cin>>name>>age;
}
void display()
{
cout<<"name: "<<name<<endl;
cout<<"age: "<<age<<endl;
}
};
class student: public people
{
private:
int score;//派生类可以对基类进行扩充
public:
void get()
{
people::get();
cin>>score;
}
void display()
{
people::display();// 调用people类(基类)里面的display成员函数
cout<<"score: "<<score<<endl;
}//覆盖(派生类可以对基类进行修改)
};
int main()
{
student a1;
a1.get();//访问派生类student里面的get函数
a1.display();//访问派生类student里面的display函数
return 0;
}
运行结果为:
3、需要继承与派生的例子
4、注意事项
-
继承方式决定了派生类对基类成员的访问权限,默认为私有继承private,最常用的是共有继承public,还有一种是保护继承protected。
-
派生类可以对基类进行扩充和修改(可参考派生类写法中的例子)。在派生类中,可以扩充新的成员变量和成员函数。
-
在派生类中声明一个和基类成员同名的成员,来“覆盖”接收的基类成员。如果想覆盖成员函数,其函数名和参数表必须相同,否则成为重载。
-
派生类也可被其他类继承,则这个派生类同时也是基类。
-
构造函数和析构函数是不能继承的,要重新定义。
-
派生类一经定义,即可不依赖基类,独立使用。
二、派生类成员的访问属性
1、不同的继承方式下,基类成员在派生类中的访问方式如下:(大多数时候使用公有继承)
- 公有继承:
基类的公有成员和保护成员在派生类中保持原有的访问属性;其私有成员仍为基类私有,派生类新增成员不可访问它。
用如下示意图来说明公有继承的访问属性:
- 私有继承:
基类的公用成员和保护成员在派生类中成为私有成员,其私有成员仍为基类私有,派生类新增成员不可访问它。 - 保护继承:
基类的公用成员和保护成员在派生类中成为保护成员,其私有成员仍为基类私有,派生类新增成员不可访问它。
特点:
对于派生类的成员而言,保护成员就像公有成员;但对于其他函数,保护成员是私有成员。
总结:
三、派生类的构造函数和析构函数
1、简单派生类的构造函数
- 即指派生类只有基类,不包含基类的对象
简单派生类的构造函数的一般形式:
派生类名(总参数表) :基类名(参数表)
{对派生类中新增数据成员初始化语句}
- 只有一个基类
PL:
class people
{
private:
string name;
int age;
public:
people(string n,int a):name(n),age(a){}
};
class student: public people
{
private:
int score;
public:
student(string n,int a,int s):people(n,a),score(s){}
};
- 具有多个基类(多重继承)
派生类名(总参数表列) :基类1构造函数(参数表),基类2构造函数(参数表),…
{对派生类中新增数据成员初始化语句}
PL:
class people//基类1
{
private:
string name;
int age;
public:
people(string n,int a):name(n),age(a){}
};
class school//基类2
{
private:
string schoolname;
public:
school(string sn):schoolname(sn){}
};
class student: public people,public school//多重派生
{
private:
int score;
public:
student(string n,int a,string sn,int s):people(n,a),school(sn),score(s){}
};
2、包含类对象的派生类的构造函数
构造函数形式:
派生类名(总参数表):基类名(参数表),对象名(参数表)
{派生类中新增数据数据成员初始化语句}
PL:
#include <iostream>
using namespace std;
class people
{
private:
string name;
int age;
public:
people(string n,int a):name(n),age(a){}
void display()
{
cout<<"name: "<<name<<endl;
cout<<"age: "<<age<<endl;
}
};
class student: public people
{
private:
int score;//派生类对基类进行扩充
people c1,c2;//people的类对象
public:
student(string n,int a,string n1,int a1,string n2,int a2,int s):people(n,a),c1(n1,a1),c2(n2,a2),score(s){}//初始化
void display_all()
{
people::display();//因为派生类无法访问基类的私有数据成员
//所以调用people类(基类)里面的display成员函数进行输出
c1.display();
c2.display();
cout<<"score: "<<score<<endl;
}//覆盖(派生类可以对基类进行修改)
};
int main()
{
student a1("zhangsan",13,"lisi",14,"wangwu",15,100);
a1.display_all();
return 0;
}
运行结果:
3、多层派生时的构造函数
多层派生时,只须写出其直接基类的构造函数即可
PL:
#include <iostream>
using namespace std;
class people
{
private:
string name;
int age;
public:
people(string n,int a):name(n),age(a){}
void display()
{
cout<<"name: "<<name<<endl;
cout<<"age: "<<age<<endl;
}
};
class student1: public people
{
private:
int score;//派生类对基类进行扩充
public:
student1(string n,int a,int s):people(n,a),score(s){}//初始化
void display_s1()
{
people::display();//因为派生类无法访问基类的私有数据成员
//所以调用people类(基类)里面的display成员函数进行输出
cout<<"score: "<<score<<endl;
}//覆盖(派生类可以对基类进行修改)
};
class student2: public student1
{
private:
string schoolname;//派生类对基类进行扩充
public:
student2(string n,int a,int s,string sn):student1(n,a,s),schoolname(sn){}//初始化
void display_s2()
{
student1::display_s1();
cout<<"schoolname: "<<schoolname<<endl;
}//覆盖(派生类可以对基类进行修改)
};
int main()
{
student2 a1("zhangsan",13,100,"zs");
a1.display_s2();
return 0;
}
运行结果:
上述程序中,基类people构造 函数为:
people(string n,int a):name(n),age(a){}
一级派生类student1构造函数为:
student1(string n,int a,int s):people(n,a),score(s){}
二级派生类student2构造函数为:
student2(string n,int a,int s,string sn):student1(n,a,s),schoolname(sn){}
即每级构造函数,只须列出其上一级的构造函数。
4、派生类构造函数和析构函数的执行顺序
-
构造函数执行顺序:
先执行基类的构造函数,初始化基类数据成员。
再执行成员对象类的构造函数,初始化成员对象类的数据成员。
最后执行派生类自身的构造函数,初始化自己新增数据成员。 -
析构函数执行顺序:
先执行派生类自己的析构函数
再依次执行各成员对象类的析构函数
最后执行基类的析构函数
即与构造函数调用顺序相反