面向对象技术强调软件的可重用性(software reusability),C++语言提供了类的继承机制,解决了软件重用问题。
C++中所谓“继承”就是在一个已存在的类的基础上建立一个新类,从已有的类那里获得已有特性,叫做类的继承。从另一角度说,从已有的类(父类)产生一个新的子类,称为类的派生。
一个派生类只从一个基类派生,这称为单继承;一个派生类有两个或多个基类,称为多继承。派生类是基类的具体化,而基类则是派生类的抽象。
一、派生类的声明方式
声明派生类的一般形式为:
class 派生类: [继承方式] 基类
{
派生类新增加的成员
};
继承方式包括:public(公用的)、private(私有的)、protected(受保护的),继承方式是可选的,如果不写此项,则默认为private(私有的)。
示例代码如下:
#include <iostream>
#include <string>
using namespace std;
// 基类 - 人类
class Person{
private:
string name; // 姓名
int age; //年龄
char gender; //性别
public:
Person(){
name = "无名氏";
age = 0;
gender = 0;
}
Person(string name, int age, char gender): name(name), age(age), gender(gender){}
};
// 派生类 - 学生
class Student: public Person{
private:
string school;
public:
void display(){}
};
int main(){
return 0;
}
二、派生类的构成
派生类中的成员包括从基类继承过来的成员和自己增加的成员两大部分,从基类继承的成员体现了派生类从基类继承而获得的共性,而新增加的成员体现了派生类的个性。
构造一个派生类包括三部分工作:
- 从基类接收成员:派生类把基类全部的成员(不包括构造函数和析构函数)接收过来,必须全部接收,不能舍弃一部分。
- 调整从基类接收的成员:接收基类成员是不可选择的,但是可以对这些成员作些调整。例如可以改变基类成员在派生类中的访问属性,这可以通过指定继承方式来实现。
- 在声明派生类时增加的成员。
三、派生类成员的访问属性
派生类中包含基类成员和派生类新增的成员,对基类成员和派生类新增的成员是按不同的原则处理的,在访问属性时,需要考虑以下几种情况:
- 基类的成员函数访问基类成员。
- 派生类的成员函数访问派生类自己增加的成员。
- 基类成员函数访问派生类的成员。
- 派生类的成员函数访问基类的成员。
- 在派生类外访问派生类的成员。
- 在派生类外访问类的成员。
在派生类中,对基类的继承方式可以有public、private、protected三种,不同的继承方式决定了基类成员在派生类中的访问属性。
- 公用继承(public inheritance):基类的公用或保护成员在派生类中保持原有访问属性,其私有成员扔为基类私有。
- 私有继承(private inheritance):基类的公用成员和保护成员在派生类中成了私有成员,其私有成员仍为基类私有。
- 受保护的继承(protected inheritance):基类的公用成员和保存成员在派生类中成了保护成员,其私有成员仍为其类私有。
3.1 公用继承
在定义一个派生类时将基类的继承方式指定为public,称为公用继承,用公用继承方式建立的派生类称为公用派生类(public derived class),其基类称为公用基类(public base class)。
公用基类的成员在派生类的访问属性如下表:
公用基类的成员 | 在公用派生类中的访问属性 |
---|---|
私有成员 | 不可访问 |
公用成员 | 公用 |
保护成员 | 保护 |
如下例中,派生类中是不能访问基类中的私有成员,代码如下:
// 基类 - 人类
class Person{
private:
string name; // 姓名
int age; //年龄
char gender; //性别
public:
Person(){
name = "无名氏";
age = 0;
gender = 0;
}
Person(string name, int age, char gender): name(name), age(age), gender(gender){}
void display(){
// 正确,基类中可以访问基类中的私有成员
cout <<name <<"," <<age <<"," <<gender <<endl;
}
};
// 派生类 - 学生
class Student: public Person{
private:
string school;
public:
void display(){
// 错误,派生类中不能访问基类中的私有成员
cout <<name <<"," <<age <<"," <<gender <<"," <<school <<endl;
}
};
如上代码,派生类Student调用基类Person中的私有成员,会报错【[Error] 'std::string Person::name' is private within this context】- 在这个上下文中,'std:string Person::name'是私有的。同理,age,gender都会报同类错误。
3.2.私有继承
在声明一个派生类时将基类的继承方式指定为private,称为私有继承,用私有继承方式建立的派生类称为私有派生类(private derived class),其基类称为私有基类(private base class)。
私有基类在派生类中的访问属性如下表:
私有基类中的成员 | 在私有派生类中的访问属性 |
私有成员 | 不可访问 |
公用成员 | 私有 |
保护成员 | 私有 |
如下列,在基类中为公用成员,在派生类继承时被指定为私有,则基类中公用成员在派生类中变有私有,外部不可调用 ,代码如下:
#include <iostream>
#include <string>
using namespace std;
// 基类 - 人类
class Person{
private:
string name; // 姓名
int age; //年龄
char gender; //性别
public:
Person(){
name = "无名氏";
age = 0;
gender = 0;
}
Person(string name, int age, char gender): name(name), age(age), gender(gender){}
// 获取名称
string getName(){
return name;
}
};
// 派生类 - 学生
class Student: private Person{
private:
string school;
public:
void display(){
// 错误,派生类中不能访问基类中的私有成员
cout <<school <<endl;
}
};
int main(){
Student s;
string name = s.getName(); // 错误,当基类被指定为私有时,基类中公用成员在派生类中变为私有,外部不可调用
cout <<name;
s.display(); //正确,派生类中新增公用成员
return 0;
}
如上代码,执行s.getName()则会报错【[Error] 'std::string Person::getName()' is inaccessible within this context】- 'std::string Person::getName()'在此上下文中不可访问。
3.3 受保护的继承
在定义一个派生类时将基类的继承方式指定为protected时,称为保护继承。用保护继承方式建立的派生类称为保护派生类(protected derived class),其基类称为保护的基类(protected base class)。
保护继承的特点是,保护基类的公用成员和保护成员在派生类中都成了保护成员,其私有成员仍为基类私有。基类成员在派生类中的访问属性如下表:
基类中的成员 | 在公用派生类中的访问属性 | 在私有派生类中的访问属性 | 在保护派生类中的访问属性 |
---|---|---|---|
私有成员 | 不可访问 | 不可访问 | 不可访问 |
公用成员 | 公用 | 私有 | 保护 |
保护成员 | 保护 | 私有 | 保护 |
保护基类的所有成员在派生类中都被保护起来,类外不能访问,其公用成员和保护成员可以被其派生类的成员函数访问。
示例代码如下 :
#include <iostream>
#include <string>
using namespace std;
// 基类 - 人类
class Person{
protected:
string name; // 姓名
int age; //年龄
char gender; //性别
// 获取名称
string getName(){
return name;
}
public:
Person(){
name = "无名氏";
age = 0;
gender = 0;
}
Person(string name, int age, char gender): name(name), age(age), gender(gender){}
};
// 派生类 - 学生
class Student: protected Person{
private:
string school;
public:
void display(){
string str_name = getName(); // 合法,派生类中可以调用基类中的保护成员
cout <<"str name:" <<str_name <<endl;
// 错误,派生类中不能访问基类中的私有成员
cout <<name <<"," <<age <<"," <<gender <<"," <<school <<endl;
}
};
int main(){
Student s;
string name = s.getName(); // 错误,当基类被指定为保护类时,基类中保护成员在派生类中可被调用,外部不可调用
cout <<name;
s.display(); //正确,派生类中新增公用成员
return 0;
}
如上代码,当s.getName()保护成员是不能被外部调用的,否则会报错【[Error] 'std::string Person::getName()' is protected within this context】- 'std::string Person::getName()'在此上下文中受保护。
3.4 多级派生时的访问属性
目前了解了一级派生的情况,但实际上,经常会有多级派生的情况。在多级派生的情况下,各成员的访问属性仍按以上原则确定。
示例代码:
#include <iostream>
using namespace std;
class A{
public:
void displayPublic(){
cout <<"A::displayPublic()" <<endl;
}
protected:
void displayProtected(){
cout <<"A::displayProtected()" <<endl;
}
private:
void displayPrivate(){
cout <<"A::displayPrivate()" <<endl;
}
};
class B: public A{
public:
void displayBPublic(){
cout <<"B::displayBPublic()" <<endl;
displayPublic();
displayProtected();
// displayPrivate(); // 错误,基类私有成员在派生类中无法访问
/**
否则会报错:[Error] 'void A::displayPrivate()' is private within this context
*/
}
};
class C: public B{
public:
void displayCPublic(){
cout <<"C::displayCPublic()" <<endl;
displayPublic();
displayProtected();
// displayPrivate(); // 错误,基类私有成员在派生类中无法访问
}
};
int main(){
B b;
b.displayPublic();
// b.displayProtected(); // 错误,保护成员在派生类外无法调用
// b.displayPrivate(); // 错误,私有成员在派生类外无法调用
b.displayBPublic();
C c;
c.displayPublic();
// c.displayProtected(); // 错误,保护成员在派生类外无法调用
// c.displayPrivate(); // 错误,私有成员在派生类外无法调用
c.displayBPublic();
c.displayCPublic();
return 0;
}
以上示例可以看出,保护成员在派生类外是无法调用的,私有成员在派生类中也无法调用。运行结果如下图:
示例分析:
- 类A中有个成员函数,分别具有public、protected、private访问属性。
- 类B公有继承类A,因此它可以访问类A的公有和保护成员,但不能访问私有成员。
- 类C公有继承类B,因此它可以访问类B的公有成员和保护成员,以及类B从类A中继承过来的公用和保护成员,但不能访问类B或类A的私有成员。