2. C++ 中的继承是⾯向对象编程的三⼤特性之⼀。
有些类与类之间存在着特殊的关系,例如下图:

我们发现,定义这些类时,下级别的成员除了拥有上⼀级的共性(特征+⾏为),还有⾃⼰的特性 (特征+⾏为)。
这个时候我们就可以考虑利⽤继承的技术。允许⼀个类(⼦类****/**** 派⽣类)继承另⼀个类(⽗类****/**** 基类)的属性和⽅法,从⽽实现代码复⽤和层次化设计。(继承允许从已有类(基类 / ⽗类)派⽣出新类(派⽣类 / ⼦类),⼦类可复⽤⽗类的成员并扩展新功能,减少代码冗余。)简单来说,继承就像:⼉⼦会继承⽗亲的某些特征(如眼睛颜⾊、⾝⾼)学⽣是⼈的⼀种,会继承 "⼈" 的基本属性(姓名、年龄),同时有⾃⼰的特有属性(学号、班级)
2.7.1 基本语法
继承⽅式: public (公有继承)、 protected (保护继承)、 private (私有继承),常⽤ public
⼦类成员包含:从⽗类继承的成员(⾮私有) + ⼦类新增的成员
2.7.2 ⽰例
例如我们看到很多⽹站中,都有公共的头部,公共的底部,甚⾄公共的左侧列表,只有中⼼内容不同。
接下来我们分别利⽤普通写法和继承写法来实现⽹⻚中的内容,看⼀下继承存在的意义以及好处。
2.7.2.1 普通实现
class 子类名 : 继承方式 父类名
{
// 子类成员
}
#include <iostream>
#include <string>
#include <iomanip>
using namespace std;
// Java方向对应的页面
class Java
{
public:
// 头部
void header()
{
cout << "首页、公开课、登录、注册...(公共头部)" << endl;
}
// 底部
void footer()
{
cout << "帮助中心、交流合作、版权信息、站内导航...(公共底部)" << endl;
}
// 左侧菜单
void left()
{
cout << "Java、Web、C++...(公共分类列表)" << endl;
}
// 右侧内容
void content()
{
cout << "Java大纲、Java学习视频、Java讲师介绍" << endl;
}
};
// Web方向对应的页面
class Web
{
public:
// 头部
void header()
{
cout << "首页、公开课、登录、注册...(公共头部)" << endl;
}
// 底部
void footer()
{
cout << "帮助中心、交流合作、版权信息、站内导航...(公共底部)" << endl;
}
// 左侧菜单
void left()
{
cout << "Java、Web、C++...(公共分类列表)" << endl;
}
// 右侧内容
void content()
{
cout << "Web大纲、Web学习视频、Web讲师介绍" << endl;
}
};
// C++方向对应的页面
class CPP
{
public:
// 头部
void header()
{
cout << "首页、公开课、登录、注册...(公共头部)" << endl;
}
// 底部
void footer()
{
cout << "帮助中心、交流合作、版权信息、站内导航...(公共底部)" << endl;
}
// 左侧菜单
void left()
{
cout << "Java、Web、C++...(公共分类列表)" << endl;
}
// 右侧内容
void content()
{
cout << "C++大纲、C++学习视频、C++讲师介绍" << endl;
}
};
int main(int argc, char **argv)
{
// Java方向
cout << "Java方向:" << endl;
Java ja;
ja.header();
ja.footer();
ja.left();
ja.content();
cout << "------------------" << endl;
// Web方向
cout << "Web方向:" << endl;
Web we;
we.header();
we.footer();
we.left();
we.content();
cout << "------------------" << endl;
// C++方向
cout << "C++方向:" << endl;
CPP cp;
2.7.2.2 继承实现
cp.header();
cp.footer();
cp.left();
cp.content();
cout << "------------------" << endl;
return 0;
}
#include <iostream>
#include <string>
#include <iomanip>
using namespace std;
// 将Java、Web、CPP中相同的代码提取到父类中
class BasePage
{
public:
// 头部
void header()
{
cout << "首页、公开课、登录、注册...(公共头部)" << endl;
}
// 底部
void footer()
{
cout << "帮助中心、交流合作、版权信息、站内导航...(公共底部)" << endl;
}
// 左侧菜单
void left()
{
cout << "Java、Web、C++...(公共分类列表)" << endl;
}
};
// 子类:Java方向对应的页面
class Java : public BasePage
{
public:
// 右侧内容
void content()
{
cout << "Java大纲、Java学习视频、Java讲师介绍" << endl;
}
};
// 子类:Web方向对应的页面
class Web : public BasePage
{
public:
// 右侧内容
void content()
{
cout << "Web大纲、Web学习视频、Web讲师介绍" << endl;
}
};
// 子类:C++方向对应的页面
class CPP : public BasePage
{
public:
// 右侧内容
void content()
{
cout << "C++大纲、C++学习视频、C++讲师介绍" << endl;
}
};
int main(int argc, char **argv)
{
// Java方向
cout << "Java方向:" << endl;
Java ja;
ja.header();
ja.footer();
ja.left();
ja.content();
cout << "------------------" << endl;
// Web方向
cout << "Web方向:" << endl;
Web we;
we.header();
we.footer();
we.left();
2.7.3 继承的好处
代码复⽤:减少重复代码,⽗类的成员可被多个⼦类共享扩展⽅便:⼦类可在⽗类基础上新增功能,⽆需修改⽗类代码
2.7.4 继承⽅式
继承的语法:
继承⽅式⼀共有三种:
公共继承【 : public 】,常⽤
保护继承【 : protected 】
私有继承【 : private 】
⽰例:
we.content();
cout << "------------------" << endl;
// C++方向
cout << "C++方向:" << endl;
CPP cp;
cp.header();
cp.footer();
cp.left();
cp.content();
cout << "------------------" << endl;
return 0;
}
class 子类 : 继承方式 父类{..}
#include <iostream>
#include <string>
#include <iomanip>
using namespace std;
// 父类
class Base1
{
public:
int public_A;
protected:
int protected_A;
private:
int private_A;
};
// 子类 公共继承(常用)
class Son1 : public Base1
{
public:
// 子类成员 访问父类成员
void func()
{
public_A = 10; // 派生类可访问父类 public 权限
protected_A = 20; // 派生类可访问父类 protected 权限
// private_A = 30; // 派生类不可访问父类 private 权限
}
};
// 外部访问测试
void test01()
{
Son1 s;
s.public_A = 100; // 类外部可访问 public 权限:类内部、派生类、类
外部均可访问
// s.protected_A = 200; // 类外部不可访问 protected 权限:类内部、派生类可
访问,类外部不可访问
// s.private_A = 300; // 类外部不可访问 private 权限:类内部可访问,派生
类和类外部不可访问
}
// ---------------------------------------------------------
// 子类 保护继承
class Son2 : protected Base1
{
public:
// 子类成员 访问父类成员
void func()
{
public_A = 10; // 派生类可访问父类 public 权限
protected_A = 20; // 派生类可访问父类 protected 权限
// private_A = 30; // 派生不可访问父类 private 权限
}
};
// 外部访问测试
void test02()
{
Son2 s;
// s.public_A = 100; // 类外部不可访问 public 权限:类内部、派生类、类
外部均可访问
// s.protected_A = 200; // 类外部不可访问 protected 权限:类内部、派生类可
访问,类外部不可访问
// s.private_A = 300; // 类外部不可访问 private 权限:类内部可访问,派生
类和类外部不可访问
}
// ----------------------------------------------
// 子类 私有继承
class Son3 : private Base1
{
public:
// 子类成员 访问父类成员
void func()
{
public_A = 10; // 派生类可访问父类 public 权限
protected_A = 20; // 派生类可访问父类 protected 权限
// private_A = 30; // 派生不可访问父类 private 权限
}
};
// 外部访问测试
void test03()
{
Son3 s;
// s.public_A = 100; // 类外部不可访问 public 权限:类内部、派生类、类
外部均可访问
// s.protected_A = 200; // 类外部不可访问 protected 权限:类内部、派生类可
访问,类外部不可访问
2.7.5 继承中构造和析构顺序
⼦类继承⽗类后,当创建⼦类对象的时候,也会调⽤⽗类构造函数。
问题:⽗类和⼦类的构造和析构顺序谁先谁后?
⽰例:
// s.private_A = 300; // 类外部不可访问 private 权限:类内部可访问,派生
类和类外部不可访问
}
int main(int argc, char **argv)
{
return 0;
}
#include <iostream>
#include <string>
#include <iomanip>
using namespace std;
// 定义父类
class Base
{
public:
Base()
{
cout << "Base构造函数" << endl;
}
~Base()
{
cout << "Base析构函数" << endl;
}
};
运⾏结果:
总结:继承中 先调⽤⽗类构造函数,再调⽤⼦类构造函数,析构顺序和构造相反。