C++继承(三种方式)
继承的概念
所谓继承,就是从“先辈”处获得特性,它是客观世界事物之间的一种重要关系。例如,脊椎动物和无脊椎动物都属于动物,在程序中便可以描述为:脊椎动物和无脊椎动物继承自动物;同时,哺乳动物和两栖动物继承自脊椎动物,而节肢动物和软体动物继承自无脊椎动物。这些动物之间会形成一个继承体系,如图 1 所示。
在 C++中,继承就是在原有类的基础上产生出新类,新类会继承原有类的所有属性和方法。
原有的类称为基类或父类,新类称为派生类或子类。派生类同样可以作为基类派生出新类。
在多层次继承结构中,派生类上一层的基类称为直接基类,隔层次的基类称为间接基类。例如在图 1 中,脊椎动物是哺乳动物的直接基类,动物是哺乳动物的间接基类。

图1 动物之间的继承体系
在 C++ 中,声明一个类继承另一个类的格式如下所示:
class 派生类名称:继承方式 基类名称
{
派生类成员声明
};
:、继承方式和基类名称。在类的继承中,有以下几点需要注意:
- 基类的构造函数与析构函数不能被继承。
- 派生类对基类成员的继承没有选择权,不能选择继承或不继承某些成员。
- 派生类中可以增加新的成员,用于实现新功能,保证派生类的功能在基类基础上有所扩展。
- 一个基类可以派生出多个派生类;一个派生类也可以继承自多个基类。
通过继承,基类中的所有成员(构造函数和析构函数除外)被派生类继承,成为派生类成员。在此基础上,派生类还可以增加新的成员。

图2 基类与派生类之间的关系
【示例1】为了让读者更好地理解和掌握继承的概念,下面通过案例演示派生类的定义与调用,C++代码如下:
#include<iostream>
using namespace std;
class Animal //定义动物类Animal
{
public:
void move(); //声明表示动物行为的成员函数move()
};
void Animal::move() //类外实现成员函数move()
{
cout<<"动物行为"<<endl;
}
class Cat:public Animal //定义猫类Cat,公有继承动物类Animal
{
public:
Cat(string name); //声明有参构造函数
void walk(); //声明表示动物行为的普通成员函数walk()
private:
string _name; //成员变量:表示名字
};
Cat::Cat(string name) //类外实现构造函数
{
_name=name;
}
void Cat::walk() //类外实现普通成员函数walk()
{
cout<<_name<<"会走"<<endl;
}
int main()
{
Cat cat("猫"); //定义猫类对象cat
cat.move(); //通过派生类对象调用基类成员函数
cat.walk(); //通过派生类对象调用新增的成员函数
return 0;
}
运行结果:
动物行为
猫会走
- 第 3~7 行代码定义了一个动物类 Animal,该类中有一个成员函数 move(),用于表示动物的行为;
- 第 12~19 行代码定义了一个猫类 Cat,该类公有继承自 Animal 类;
- 第 30 行代码,在 main() 函数中创建了猫类对象 cat;
- 第 31 行代码,通过对象 cat 调用基类成员函数 move();
- 第 32 行代码,通过对象 cat 调用 Cat 类成员函数 walk()。
本例 Cat 类中并没有定义 move() 函数,但是 Cat 类继承了 Animal 类,它会继承 Animal 类的 move() 函数,因此 Cat 类对象能够调用 move() 函数。

图3 Cat类与Animal类的继承关系
需要注意的是,在图 3 中,空心箭头表示继承关系;
+符号表示成员访问权限为public(公有继承),−符号表示成员访问权限为 private(私有继承)。如果成员访问权限为 protected(保护继承)或友元,则用
#符号表示。继承方式
在继承中,派生类会继承基类除构造函数、析构函数之外的全部成员。从基类继承的成员,其访问属性除了成员自身的访问属性,还受继承方式的影响。类的继承方式主要有三种:public(公有继承)、protected(保护继承)和 private(私有继承)。
不同的继承方式会影响基类成员在派生类中的访问权限。下面分别介绍这三种继承方式。
1) public(公有继承)
采用公有继承方式时,基类的公有成员和保护成员在派生类中仍然是公有成员和保护成员,其访问属性不变,可以使用派生类的对象访问基类公有成员。但是,基类的私有成员在派生类中变成了不可访问成员。
如果基类中有从上层基类继承过来的不可访问成员,则基类的不可访问成员在它的派生类中同样是不可访问的。
| 基类成员访问属性 | public | protected | private | 不可访问 |
|---|---|---|---|---|
| 在派生类中的访问属性 | public | protected | 不可访问 | 不可访问 |
注意:不可访问成员是指无论在类内还是在类外均不可访问的成员。它与私有成员的区别是,私有成员在类外不可访问,只能通过类的成员进行访问。不可访问成员完全是由类的派生形成的。对于顶层类,不存在不可访问成员,但是通过继承,基类的私有成员在派生类内就成为不可访问成员。
【示例2】下面通过案例演示类的公有继承,C++ 代码如下:
#include<iostream>
using namespace std;
class Student //定义学生类Student
{
public:
void setGrade(string grade); //设置年级的成员函数
string getGrade(); //获取年级的成员函数
void setName(string name); //设置姓名的成员函数
string getName(); //获取姓名的成员函数
protected:
string _grade; //保护成员:表示年级
private:
string _name; //私有成员:表示姓名
};
void Student::setGrade(string grade) //类外实现setGrade()函数
{
_grade=grade;
}
string Student::getGrade() //类外实现getGrade()函数
{
return _grade;
}
void Student::setName(string name) //类外实现setName()函数
{
_name=name;
}
string Student::getName() //类外实现getName()函数
{
return _name;
}
class Undergraduate:public Student //大学生类公有继承学生类
{
public:
Undergraduate(string major); //声明构造函数
void show(); //声明显示大学生信息的成员函数
private:
string _major; //私有成员:表示专业
};
//类外实现构造函数
Undergraduate::Undergraduate(string major)
{
_major=major;
}
void Undergraduate::show() //类外实现show()函数
{
cout<<"姓名:"<<getName()<<endl; //派生类调用基类成员函数
cout<<"年级:"<<_grade<<endl; //派生类访问继承的基类成员变量
cout<<"专业:"<<_major<<endl; //派生类访问新增成员
}
int main()
{
//创建大学生类对象stu
Undergraduate stu("计算机信息工程");
stu.setGrade("大三"); //派生类对象调用基类成员函数设置年级
stu.setName("www.weixueyuan.net"); //派生类对象调用基类成员函数设置姓名
stu.show(); //派生类对象调用新增成员函数显示学生信息
return 0;
}
运行结果:
姓名:www.weixueyuan.net
年级:大三
专业:计算机信息工程
- 第 3~14 行代码定义了学生类 Student,该类声明了私有成员变量 _name 表示姓名,保护成员变量 _grade 表示年级。Student 类还定义了 4 个公有成员函数,分别用于设置、获取学生姓名和年级;
- 第 31~38 行代码定义大学生类 Undergraduate 公有继承 Student 类。Undergraduate 类定义了私有成员变量 major 表示学生专业,此外,还定义了构造函数和显示学生信息的 show() 函数;
- 第 53~55 行代码,在 main() 函数中创建 Undergraduate 类对象 stu,并通过对象 stu 调用基类的 setGrade() 函数、setName() 函数,用来设置学生的年级和姓名;
- 第 56 行代码通过对象 stu 调用 show() 函数显示学生信息。
注意:本例第 46~47 行代码,在 Undergraduate 类的 show() 函数内部直接访问了从基类继承过来的保护成员 _grade,因为 Undergraduate 类是公有继承 Student 类,_grade 在派生类 Undergraduate 中也是保护成员,所以可以通过成员函数 show() 访问。
但是,show() 函数无法直接访问从基类继承过来的 _name 成员,因为 _name 是基类的私有成员,在派生类中,_name 变成了派生类的不可访问成员。所以在 show() 函数中只能通过基类的公有成员函数 getName() 访问 _name 成员。
如果在 show() 函数中直接访问从基类继承过来的 _name 成员,程序会报错。
例如,若在 show() 函数中添加如下代码:
cout<<_name<<endl;
再次运行程序,编译器会报错,如下所示:
"Student::_name":无法访问 private 成员(在"Student"类中声明)
成员"Student::name"(已声明所在行数:13)不可访问
Undergraduate类与Student类之间的公有继承关系可以用图 4 表示。

图4 Undergraduate类公有继承Student类
2) protected(保护继承)
采用保护继承方式时,基类的公有成员和保护成员在派生类中全部变成保护成员,派生类的其他成员可以直接访问它们,在派生类外无法访问。基类的私有成员和不可访问成员在派生类中的访问属性是不可访问。
| 基类成员访问属性 | public | protected | private | 不可访问 |
|---|---|---|---|---|
| 在派生类中的访问属性 | protected | protected | 不可访问 | 不可访问 |
若将【示例2】中第 31 行代码的继承方式改为 protected,再次运行程序,此时编译器会报错,如下所示:
"Student::setGrade"不可访问,因为"UnderGraduate"使用"protected"从"Student"继承
"Student::setName"不可访问,因为"UnderGraduate"使用"protected"从"Student"继承
函数"Student::setGrade"(已声明所在行数:15)不可访问
函数"Student::setName"(已声明所在行数:23)不可访问
由于 Undergraduate 保护继承 Student 类,Student 类的 setGrade() 函数和 setName() 函数就变成了 Undergraduate 类的保护成员,保护成员在类外不能访问,因此编译器会报错。
Undergraduate 类与 Student 类之间的保护继承关系可以用图 5 表示。

图5 Undergraduate类保护继承Student类
3) private(私有继承)
采用私有继承方式时,基类的公有成员和保护成员在派生类中全部变成私有成员,派生类的其他成员可以直接访问它们,在派生类外无法访问。基类的私有成员和不可访问成员在派生类中的访问属性是不可访问。
| 基类成员访问属性 | public | protected | private | 不可访问 |
|---|---|---|---|---|
| 在派生类中的访问属性 | private | private | 不可访问 | 不可访问 |
与保护继承相比,在直接派生类中,私有继承与保护继承的作用实际上是相同的。
在派生类外,不可访问任何基类成员;在派生类内,可以通过其他成员访问继承的基类公有成员和保护成员。
但是,如果再以派生类为基类派生新类,对于保护继承方式,派生类中的保护成员在新类中仍然是保护成员,类内的其他成员可以访问。
对于私有继承方式,派生类中的私有成员在新类中变成了不可访问成员,实际上就终止了基类功能在派生类中的延伸。
类型兼容
不同类型的数据在一定条件下可以进行转换,比如 int n='a',是将字符 'a' 赋值给整型变量 n,在赋值过程中发生了隐式类型转换,字符类型的数据转换为整型数据。这种现象称为类型转换,也称为类型兼容。在 C++ 中,基类与派生类之间也存在类型兼容。通过公有继承,派生类获得了基类除构造函数、析构函数之外的所有成员。公有派生类实际上就继承了基类所有公有成员。
因此,在语法上,公有派生类对象总是可以充当基类对象,即可以将公有派生类对象赋值给基类对象,在用到基类对象的地方可以用其公有派生类对象代替。
C++ 中的类型兼容情况主要有以下几种:
- 使用公有派生类对象为基类对象赋值。
- 使用公有派生类对象为基类对象的引用赋值。
- 使用公有派生类对象的指针为基类指针赋值。
- 如果函数的参数是基类对象、基类对象的引用、基类指针,则函数在调用时,可以使用公有派生类对象、公有派生类对象的地址作为实参。
【示例3】为了让读者更深入地理解 C++ 类型兼容规则,下面通过案例演示基类与派生类之间的类型兼容,程序代码如下:
#include<iostream>
using namespace std;
class Base //定义基类Base
{
public:
Base(); //Base类构造函数
void show(); //Base类普通成员函数show()
protected:
string _name; //Base类保护成员变量_name
};
Base::Base() //类外实现基类构造函数
{
_name="base";
}
void Base::show() //类外实现show()函数
{
cout<<_name<<" Base show()"<<endl;
}
class Derive:public Base //Derive类公有继承Base类
{
public:
Derive(); //Derive类构造函数
void display(); //Derive类普通成员函数display()
};
Derive::Derive() //类外实现派生类构造函数
{
_name="derive"; //_name成员从Base类继承而来
}
void Derive::display() //类外实现display()函数
{
cout<<_name<<" Derive show()"<<endl;
}
void func(Base* pbase) //定义普通函数func(),参数为基类指针
{
pbase->show();
}
int main()
{
Derive derive; //创建Derive类对象derive
Base base=derive; //使用对象derive为Base类对象base赋值
Base &qbase=derive; //使用对象derive为Base类对象的引用qbase赋值
Base *pbase=&derive; //使用对象derive的地址为Base类指针pbase赋值
base.show(); //通过Base类对象调用show()函数
qbase.show(); //通过Base类对象的引用调用show()函数
pbase->show(); //通过Base类指针调用show()函数
func(&derive); //取对象derive的地址作为func()函数的参数
return 0;
}
运行结果:
derive Base show()
derive Base show()
derive Base show()
derive Base show()
- 第 3~10 行代码定义了 Base 类,该类有一个保护成员变量 _name,此外 Base 类还定义了构造函数和普通成员函数 show();
- 第 19~24 行代码定义了 Derive 类,Derive 类公有继承 Base 类,Derive 类中定义了构造函数和普通成员函数 display();
- 第 33~36 行代码定义了一个函数 func(),该函数有一个 Base 类的指针作为参数,在函数内部,通过 Base 类指针调用 show() 函数;
- 第 39 行代码,在 main() 函数中创建了 Derive 类对象 derive;
- 第 40 行代码创建 Base 类对象 base,使用对象 derive 为其赋值;
- 第 41 行代码创建 Base 类对象的引用,使用 derive 对象为其赋值;
- 第 42 行代码定义 Base 类指针,取对象 derive 的地址为其赋值;
- 第 43~45 行代码分别通过 Base 类对象、Base 类对象的引用、Base 类指针调用 show() 函数;
- 第 46 行代码调用 func() 函数,并取对象 derive 的地址作为实参传递。
由运行结果可知,使用对象 derive 代替 Base 类对象,程序成功调用了 show() 函数。Derive 类与 Base 类的继承关系如图 6 所示。

图6 Derive类与Base类的继承关系
需要注意的是,虽然可以使用公有派生类对象代替基类对象,但是通过基类对象只能访问基类的成员,无法访问派生类的新增成员。
如果在【示例3】中,通过基类对象 base、基类对象的引用 qbase、基类指针 pbase 访问 display() 函数,示例代码如下:
base.display();
qbase.display();
pbase->display();
“display”:不是“Base”的成员
“display”:不是“Base”的成员
“display”:不是“Base”的成员
声明:《C++系列教程》为本站“54笨鸟”官方原创,由国家机构和地方版权局所签发的权威证书所保护。
ICP备案:
公安联网备案: