目录
一、继承基本方式
1、公 共 继 承
2、保 护 继 承
3、私 有 继 承
二、继承中的对象模型
三、继承中的构造和析构顺序
四、继承中同名成员处理方式
1、成员变量的处理方式
2、成员函数的处理方式
五、继承同名静态成员处理方式
1、通过对象进行访问
2、通过类名进行访问
3、同名静态成员函数访问
六、多继承语法
七、菱形继承问题
1、二义性问题
2、资源浪费问题
继承引入:
继承是面向对象的三大特性之一(封装、继承、多态)
下级别成员除了拥有上一级的共性,还有自己的特性。
可以使用继承的技术,减少重复代码
基类 (父类)
派生类(子类)
继承的语法:class 子类 : 继承方式 父类
-
继承方式:
- 公共继承
- 保护继承
- 私有继承
所有继承方式私有内容都是访问不到
(除私有权限其余原封不动)
父类 公共权限 到 子类 还是 公共权限
父类 保护权限 到 子类 还是 保护权限
父类 私有权限 到 子类 变成 不可访问
(除私有权限其余变成保护权限)
父类 公共权限 到 子类 变成 保护权限
父类 保护权限 到 子类 还是 保护权限
父类 私有权限 到 子类 变成 不可访问
(除私有权限其余变成私有权限)
父类 公共权限 到 子类 变成 私有权限
父类 保护权限 到 子类 变成 私有权限
父类 私有权限 到 子类 变成 不可访问
该关系可用于多次嵌套继承!!!
问题:从父类继承过来的成员,哪些属于子类对象中?
利用 sizeof 关键字进行测试
测试代码:
#include
using namespace std;
class Base
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son :public Base
{
public:
int m_D;
};
void test()
{
Son s;
cout << "sizeof of Son = " << sizeof(s) << endl;
}
int main()
{
test();
system("pause");
return 0;
}
运行结果:
结论:
父类中所有非静态成员属性都会被子类继承下去
-
父类中私有属性被编译器隐藏,所以访问不到,但实际被继承下去
使用 vs 开发人员命令提示符工具 进行测试
找到所在文件夹
将文件所在路径进行复制
在工具内写入 cd + 空格 + 路径 然后回车(文件不在当前盘需要跳转盘符 C:)
dir 浏览当前文件夹下的文件
输入 cl 空格 /d1空格 reportSingleClassLayout+类名 空格 + cpp文件(继承.cpp)
(报告单个类的布局)
子类继承父类后,当创建子类对象,也会调用父类的析构函数
-
问题:父类和子类的构造和析构顺序谁先?
测试代码:
#include
using namespace std;
class Base{
public:
Base() {
cout << "父类的构造函数" << endl;
}
~Base() {
cout << "父类的析构函数" << endl;
}
};
class Son :public Base{
public:
Son() {
cout << "子类的构造函数" << endl;
}
~Son() {
cout << "子类的析构函数" << endl;
}
};
void test(){
// 子类实例化
Son s;
}
int main(){
test();
system("pause");
return 0;
}
运行结果:
结论: 先构造父类再构造子类,先析构子类再析构父类
当子类与父类出现同名成员,如何通过子类对象访问到子类或父类中的同名数据?
- 访问子类同名成员,可以直接访问,也可以加作用域
- 访问父类同名成员,需要加作用域
访问子类成员
测试代码:
#include
using namespace std;
class Base{
public:
Base()
{
m_A = 100;
}
int m_A;
};
class Son :public Base{
public:
Son() {
m_A = 200;
}
int m_A;
};
void test(){
// 子类实例化
Son s;
cout << s.m_A << endl;
}
int main(){
test();
system("pause");
return 0;
}
运行结果:
子类对象访问父类成员,加上父类作用域 类名::
结论:访问子类同名成员变量,可以直接访问
测试代码:
#include
using namespace std;
class Base{
public:
void func() {
cout << "父类成员函数调用" << endl;
}
void func(int a) {
cout << "父类成员函数重载调用" << endl;
}
};
class Son :public Base{
public:
void func() {
cout << "子类成员函数调用" << endl;
}
};
void test(){
// 子类实例化
Son s;
s.func(); // 子类成员函数
s.Base::func(); // 父类成员函数
s.Base::func(100); // 父类成员函数重载
}
int main(){
test();
system("pause");
return 0;
}
运行结果:
结论:如果子类中出现和父类同名的成员函数,子类的同名成员函数会隐藏父类中的所有同名成员函数,想要访问就要加作用域
继承中同名静态成员在子类对象上如何进行访问?
(和静态成员处理方式一致)
同名静态成员变量有两种访问方式
测试代码:
#include
using namespace std;
class Base{
public:
static int m_A; // 静态成员变量,类内声明类外初始化
};
// 类外初始化
int Base::m_A = 100;
class Son :public Base{
public:
static int m_A; // 静态成员变量,类内声明类外初始化
};
// 类外初始化
int Son::m_A = 200;
void test(){
// 子类实例化
Son s;
// 通过对象进行访问
cout << s.m_A << endl; // 使用子类中的静态成员变量
cout << s.Base::m_A << endl; // 使用父类中的静态成员变量
}
int main(){
test();
system("pause");
return 0;
}
运行结果:
测试代码:
#include
using namespace std;
class Base{
public:
static int m_A; // 静态成员变量,类内声明类外初始化
};
// 类外初始化
int Base::m_A = 100;
class Son :public Base{
public:
static int m_A; // 静态成员变量,类内声明类外初始化
};
// 类外初始化
int Son::m_A = 200;
void test(){
// 子类实例化
Son s;
// 通过类名进行访问
cout << Base::m_A << endl; // 父类成员函数
cout << Son::m_A << endl; // 子类成员函数
}
int main(){
test();
system("pause");
return 0;
}
运行结果:
也可以通过子类对象访问父类对象再访问父类对象中的成员变量
cout << Son::Base::m_A << endl;
第一个 :: 表示通过类名方式访问
第二个 :: 表示通过访问父类作用域下的成员变量
测试代码:
#include
using namespace std;
class Base{
public:
static void func() {
cout << "父类静态成员函数调用" << endl;
}
static void func(int a) {
cout << "父类静态成员函数重载调用" << endl;
}
};
class Son :public Base{
public:
static void func() {
cout << "子类静态成员函数调用" << endl;
}
};
void test(){
// 子类实例化
Son s;
s.func(); // 子类静态成员函数
s.Base::func(); // 父类静态成员函数
s.Base::func(100);// 父类静态成员函数重载
}
int main(){
test();
system("pause");
return 0;
}
运行结果:
结论:如果子类中出现和父类同名的成员函数,子类的同名成员函数会隐藏父类中的所有同名成员函数,想要访问就要加作用域
C++允许一类继承多个类
-
语法:class 子类 :继承方式 父类1,继承方式 父类2 .....
-
多继承可能引发父类中同名成员出现,所以需要加作用域区分
-
实际开发不建议使用多继承
#include
using namespace std;
class Base1 {
public:
Base1() {
m_A = 100;
}
int m_A;
};
class Base2 {
public:
Base2() {
m_A = 200;
}
int m_A;
};
class Son :public Base1, public Base2 {
public:
Son() {
m_A = 300;
}
int m_A;
};
void test() {
Son s;
cout << s.m_A << endl; // 子类成员变量
cout << s.Base1::m_A << endl; // 父类1 成员变量
cout << s.Base2::m_A << endl; // 父类2 成员变量
}
int main() {
test();
system("pause");
return 0;
}
运行结果:
结论:如果父类中出现同名情况,子类使用时候要加作用域
菱形继承(钻石继承)概念:
-有一个基类
-两个派生类继承上述基类
-又有一个类同时继承上述两个派生类
问题:
- 类2 继承了 类1 的数据,类3 也继承了 类1 的数据,当 类4 使用数据时就会产生二义性
- 类4 继承了两份 类1 的数据,造成浪费
通过 Person4 p4 对象访问 子类 中的成员变量时出现二义性
测试代码:
#include
using namespace std;
class Person1{
public:
int m_Age;
};
class Person2 :public Person1{
public:
};
class Person3 :public Person1{
public:
};
class Person4 :public Person2, public Person3{
public:
};
void test(){
Person4 p4;
p4.Person2::m_Age = 18;
p4.Person3::m_Age = 19;
cout << "Person2 的age = " << p4.Person2::m_Age << endl;
cout << "Person3 的age = " << p4.Person3::m_Age << endl;
}
int main(){
test();
system("pause");
return 0;
}
运行结果:
结论:当出现菱形继承,可以加作用域区分
Person4 这个类中有两个 m_Age
虚继承解决菱形继承问题
继承之前加上关键字 virtual 转化为虚继承
Person1 类 变为虚类
通过 vs工具 可以看到只有一个 成员变量
查看类成员的时候发现从虚基类Person2 和 虚基类Person3 继承过来的是vbptr
--------------------------------
v --- virtual (虚拟的)|
b --- base (基础) |
ptr --- pointer(指针) |
--------------------------------
vbptr(虚基类指针) 指向 vbtable (虚基类表)