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笨鸟”官方原创,由国家机构和地方版权局所签发的权威证书所保护。