① 继承,就是从先辈处得到属性和行为特征,类的继承就是新的类从已有类那里得到已有的特性。
② 类的派生,即可看作从已有类产生新类的过程。由已有类产生新类时,新类便包含了已有类的特征,同时也可以加入自己的新特性。
③ 已有类称为 基类 或 父类 ,产生的新类称为 派生类 或 子类。派生类同样也可以作为基类派生出新的类,这样就形成了类的层次结构。
④ 类的继承和派生,使程序员无需修改已有类,只需在已有类的基础上,通过增加少量代码或修改少量代码的方法得到新的类,从而较好的解决代码重用的问题。
一、派生类的声明
声明一个派生类的一般格式为:
class 派生类名:[继承方式] 基类名{
派生类新增的数据成员和成员函数
};
这里的 “基类名” 是一个已经声明的类的名称,“派生类名” 是继承原有类的特性而生成的新类的名称。“继承方式” 规定了如何访问从基类继承的成员,它可以是关键字 private,protected 或 public,分别表示私有继承,保护继承和公有继承,如果不显式地给出继承方式关键字,系统默认为私有继承。类的继承方式指定了派生类成员以及类外对象对于从基类继承来的成员的访问权限。
例 1 : 现有一个 Person 类,包含 name(姓名)、age(年龄)、sex(性别)等数据成员与成员函数 print:
#include<iostream>
using namespace std;
class Person{
public:
···
void print(){
cout<<"name:"<<name<<endl;
cout<<"age:"<<age<<endl;
cout<<"sex:"<<sex<<endl;
}
protected: //保护成员可以被本类的成员函数访问,也可以被本类的派生类的成员函数访问
string name;
int age;
char sex;
};
要声明一个 Employee 类,它包含有 name(姓名)、age(年龄)、sex(性别)、department(部门)及 salary(工资)等数据成员与成员函数 print1,如下所示:
class Employee:public Person{ //声明派生类 Employee 公有继承了基类 Person
public:
void print1(){ //新增加的函数成员
print();
cout<<"department:"<<department<<endl;
cout<<"salary:"<<salary<<endl;
}
private:
string department; //新增加的数据成员
float salary; //新增加的数据成员
};
二、派生类的构成
实际上,并不是把基类的成员和派生类新增加的成员简单地加在一起就构成了派生类。构造一个派生类一般包括以下 3 部分工作:
(1)派生类从基类接收成员
在 C++ 的类继承中,派生类把基类的全部成员(除构造函数和析构函数之外)接收过来。
(2)调整从基类接收的成员
派生类不能对接收基类的成员进行选择,但是可以对这些成员进行某些调整。对基类成员的调整包括两个方面:
① 改变基类成员在派生类中的访问属性,这主要是通过派生类声明时的继承方式来控制的。
② 派生类可以对基类的成员进行重定义,即在派生类中声明一个与基类成员同名的成员,则派生类中的新成员会覆盖基类的同名成员,这时在派生类中或者通过派生类对象,直接使用成员名就只能访问到派生类中声明的同名成员。
注意:在 ② 中,如果是成员函数,不仅应使函数名相同,而且函数的参数表也应相同,如果不相同,则称派生类重载了基类的成员函数,而不是覆盖了基类的同名函数。
(3)在派生类中增加新的成员
这体现了派生类对基类功能的扩展,是继承和派生机制的核心,来实现必要的新增功能。
由于在继承过程中,基类的构造函数和析构函数是不能被继承的,因此在声明派生类时,一般需要在派生类中定义新的构造函数和析构函数(当然,如果没有定义则会调用系统自动默认生成的构造函数和析构函数)。
三、基类成员在派生类中的访问属性(最小原则)
类的继承方式有 public(公有继承)、protected(保护继承)和 private(私有继承)3种,不同的继承方式导致不同访问属性的基类成员在派生类中的访问属性也不同。
在派生类中,从基类继承来的成员可以按访问属性划分为 4 种:不可直接访问,公有(public),保护(protected)和私有(private)。
基类中的成员 | 在公有派生类中的访问属性 | 在私有派生类中的访问属性 | 在保护派生类中的访问属性 |
---|---|---|---|
私有成员 | 不可直接访问 | 不可直接访问 | 不可直接访问 |
公有成员 | 公有 | 私有 | 保护 |
保护成员 | 保护 | 私有 | 保护 |
例如,当类的继承方式为私有继承时,基类中的所有公有成员在派生类中都以私有成员的身份出现。
四、派生类对基类成员的访问规则
基类的成员可以有 public(公有)、protected(保护)和 private(私有)3种访问属性,基类的成员函数可以访问基类中其他成员,但是在类外通过基类的对象,就只能访问该基类的公有成员。
我们知道类的继承方式有 public(公有继承)、protected(保护继承)和 private(私有继承)3种。不同的继承方式导致原来具有不同访问属性的基类成员在派生类中的访问属性也有所不同。
派生类对基类成员的访问形式主要有以下两种:
(1)内部访问。由派生类中新增的成员函数对基类继承来的成员的访问。
(2)对象访问。在派生类外部,通过派生类的对象对从基类继承来的成员的访问。
1、私有继承的访问规则
当类的继承方式为私有继承时,基类的公有成员和保护成员被继承后作为派生类的私有成员,派生类的成员函数可以直接访问它们,但是在类外部通过派生类的对象无法访问。
基类的私有成员不允许派生类继承,因此在私有派生类中是不可直接访问的,所以无论是派生类成员函数还是通过派生类的对象,都无法直接访问从基类继承来的私有成员。
私有继承的访问规则:
基类中的成员 | 私有成员 | 公有成员 | 保护成员 |
---|---|---|---|
内部访问 | 不可访问 | 可访问 | 可访问 |
对象访问 | 不可访问 | 不可访问 | 不可访问 |
例 1:私有继承的访问规则
#include<iostream>
using namespace std;
class Base{ //声明基类 Base
public:
void setx(int n){ //正确,成员函数 setx 可以访问本类的私有成员 x
x=n;
}
void showx(){ //正确,成员函数 showx 可以访问本类的私有成员 x
cout<<x<<endl;
}
private:
int x;
};
class Derived:private Base{ //声明基类 Base 的私有派生类 Derived
private:
int y;
public:
void setxy(int n,int m){
setx(n); //基类的 setx 函数在派生类中为私有成员,派生类成员函数可以访问
y=m; //正确,成员函数 setxy 可以访问本类的私有成员 y
}
void showxy(){
// cout<<x; //错误,派生类成员函数不能直接访问基类的私有成员
cout<<y<<endl;
}
};
int main(){
Derived obj;
// obj.setx(10); //错误,setx 在派生类中为私有成员,派生类对象不能访问
// obj.showx(); //错误
obj.setxy(20,30); //正确
obj.showxy(); //正确
return 0;
}
例 2:私有继承的访问规则2
#include<iostream>
using namespace std;
class Base{ //声明基类 Base
protected:
int a;
public:
void seta(int sa){ //正确,成员函数 seta 可以访问本类的保护成员 a
a=sa;
}
void showa(){ //正确,成员函数 showa 可以访问本类的保护成员 a
cout<<"a="<<a<<endl;
}
};
class Derive01:private Base{ //声明基类 Base 的私有派生类 Derive01
protected:
int b;
public:
void setab(int sa,int sb){
a=sa; // a 在派生类中为私有成员,派生类成员函数可以访问
b=sb;
}
void showab(){
cout<<"a="<<a<<endl;
cout<<"b="<<b<<endl;
}
};
class Derive02:private Derive01{ //声明类 Derive01 的私有派生类 Derive02
private:
int c;
public:
void setabc(int sa,int sb,int sc){
setab(sa,sb);
c=sc;
}
void showabc(){
//cout<<"a="<<a<<endl; //错误,a 在类 Derive02 中为不可直接访问成员
//cout<<"b="<<b<<endl; //正确,b 在类 Derive02 中为私有成员
showab();
cout<<"c="<<c<<endl;
}
};
int main(){
Base op1;
op1.seta(1);
op1.showa();
Derive01 op2;
op2.setab(2,3);
op2.showab();
Derive02 op3;
op3.setabc(4,5,6);
op3.showabc();
return 0;
}
2、公有继承的访问规则
基类中的成员 | 私有成员 | 公有成员 | 保护成员 |
---|---|---|---|
内部访问 | 不可访问 | 可访问 | 可访问 |
对象访问 | 不可访问 | 可访问 | 不可访问 |
例 3:公有继承的访问规则举例
#include<iostream>
using namespace std;
class Base{
private:
int x;
protected:
int y;
public:
void setxy(int m,int n){
x=m;
y=n;
}
void showxy(){
cout<<"x="<<x<<endl;
cout<<"y="<<y<<endl;
}
};
class Derived:public Base{ //声明基类 Base 的公有派生类 Derived
private:
int z;
public:
void setxyz(int m,int n,int l){
setxy(m,n); //函数 setxy 在派生类中是 public 成员,派生类成员函数可以访问
z=l;
}
void showxyz(){
//cout<<"x="<<x<<endl; //错误,x 在类 Derived 中为不可直接访问成员
//cout<<"y="<<y<<endl; //正确,y 在类 Derived 中为保护成员,派生类成员函数可以访问
showxy();
cout<<"z="<<z<<endl;
}
};
int main(){
Derived obj;
obj.setxyz(30,40,50);
obj.showxy(); //正确,函数 showxy()在类 Derived 中为公有成员,派生类对象能访问它
//obj.y=60; //错误,y 在类 Derived 中为保护成员,派生类对象不能访问它
obj.showxyz();
return 0;
}
3、保护继承的访问规则
基类中的成员 | 私有成员 | 公有成员 | 保护成员 |
---|---|---|---|
内部访问 | 不可访问 | 可访问 | 可访问 |
对象访问 | 不可访问 | 不可访问 | 不可访问 |
例 4:保护继承的访问规则举例
#include<iostream>
using namespace std;
class Base{ //声明基类 Base
private:
int x;
protected:
int y;
public:
int z;
void setx(int i){
x=i;
}
int getx(){
return x;
}
};
class Derived:protected Base{
private:
int m;
protected:
int n;
public:
int p;
void setall(int a,int b,int c,int d,int e,int f);
void show();
};
void Derived::setall(int a,int b,int c,int d,int e,int f){
// x=a; //错误,改为 setx(a);
setx(a);
y=b;
z=c;
m=d;
n=e;
p=f;
}
void Derived::show(){
// cout<<"x="<<x<<endl; //错误,在派生类 Derived 中,x 为不可直接访问成员
cout<<"x="<<getx()<<endl;
cout<<"y="<<y<<endl;
cout<<"z="<<z<<endl;
cout<<"m="<<m<<endl;
cout<<"n="<<n<<endl;
cout<<"p="<<p<<endl;
}
int main(){
Derived obj;
obj.setall(1,2,3,4,5,6);
obj.show();
//cout<<"y="<<obj.y<endl; //错误,y 为派生类 Derived 的保护成员,派生类对象不能访问它
return 0;
}