继承是面向对象三大特性之一 定义类时,下级别的成员除了拥有上一级的共性,还有自己的特性,就可以考虑使用继承的技术,减少代码的重复
继承的基本语法
语法:class 子类 : 继承方式 父类
- 子类也被成为派生类
- 父类也被称为基类
class A
{
public:
string name;
};
class B :public A
{
public:
int age;
};
int main()
{
B b;
b.name = "张三";
b.age = 10;
cout << b.name << b.age << endl;
return 0;
}
继承方式
继承方式一共有三种:
- 公共继承
- 访问权限不变
- 保护继承
- 除私有内容外,都变为保护权限
- 私有继承
- 除私有内容外,都变为私有权限
父类中的私有内容,三种继承方法都无法访问
class A
{
public:
int a;
protected:
int b;
private:
int c;
};
class B :public A//公共继承
{
};
class C :protected A//保护继承
{
};
class D :private A//私有继承
{
};
继承中的对象模型
父类中所有非静态成员属性都会被子类继承下去 父类中私有的成员属性,是被编译器给隐藏了,因此是访问不到,但是确实被继承下去了 利用开发人员命令提示工具查看对象模型:
- 跳转盘符:
盘符:
- 跳转文件路径:
cd 具体路径下
- 查看命名:
dir
- 报告单个类的布局:
cl /d1 reportSingleClassLayout类名 文件名
文件名可按Tap建自动补齐
class A
{
public:
int a;
protected:
int b;
private:
int c;
};
class B :public A//公共继承
{
int c;
};
class B size(16):
+---
0 | +--- (base class A)
0 | | a
4 | | b
8 | | c
| +---
12 | c
+---
继承中构造和析构顺序
先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反
继承同名成员处理方式
子类对象可以直接访问到子类中的同名成员 子类对象加作用域可以访问到父类同名成员 当子类与父类拥有同名的成员函数,子类会隐藏父类中所有同名成员函数,加作用域可以访问到父类中同名函数
class A
{
public:
void test()
{
cout << "A" << endl;
}
};
class B :public A//公共继承
{
public:
void test()
{
cout << "B" << endl;
}
};
int main()
{
B b;
b.test();
b.A::test();
return 0;
}
继承同名静态成员处理方式
静态成员跟非静态成员出现同名,处理方法一致,只不过有两种处理方法:
- 通过对象
.
- 通过类名
::
class A
{
public:
static string a;
};
class B :public A//公共继承
{
public:
static string a;
};
//类内声明,类外初始化
string B::a = "B";
string B::A::a = "A";
int main()
{
//通过对象访问
B b;
cout << b.a << endl;
cout << b.A::a << endl;
//通过类名访问
cout << B::a << endl;
//第一个::表示通过类名方式访问,第二个::代表访问父类作用域下
cout << B::A::a << endl;
return 0;
}
多继承语法
C++允许一个类继承多个类
语法:class 子类 : 继承方式 父类1 , 继承方式 父类2...
多继承可能会引发父类中有同名成员出现,需要加作用域区分
C++实际开发中不建议用多继承
class A
{
public:
int a;
};
class B
{
public:
int a;
};
class C :public A, public B
{
};
int main()
{
C c;
c.A::a = 10;
c.B::a = 20;
cout << c.A::a << endl;
cout << c.B::a << endl;
return 0;
}
菱形继承
菱形继承概念:
- 两个派生类继承同一个基类
- 又有某个类同时继承这两个派生类
- 这种继承被称为菱形继承,也被称为钻石继承
典型的菱形继承问题: 菱形继承问题:子类继承两份相同的数据,导致资源浪费以及毫无意义
- 羊继承了动物的数据,驼也继承了动物的数据,当羊驼使用数据时,就会产生二义性
- 羊驼继承自动物的数据继承了两份,只需要一份就可以
利用虚继承,解决菱形继承的问题:
- 继承之前,加上关键字
virtual
变为虚继承 - 公共的父类被称为虚基类
class A
{
public:
int a;
};
//A为虚基类
class B :virtual public A{};
class C :virtual public A{};
class D:public B,public C{};
int main()
{
D d;
d.a = 10;
cout << d.a << endl;
return 0;
}
vbptr虚基类指针:
- v-virtual
- b-base
- ptr-pointer
虚基类指针指向vbtable虚基类表
- 实际继承了两个指针,通过偏移量,找到那份唯一的数据
class D size(24):
+---
0 | +--- (base class B)
0 | | {vbptr}
| | <alignment member> (size=4)
| +---
8 | +--- (base class C)
8 | | {vbptr}
| | <alignment member> (size=4)
| +---
| <alignment member> (size=4)
+---
+--- (virtual base A)
16 | a
+---
D::$vbtable@B@:
0 | 0
1 | 16 (Dd(B+0)A)
D::$vbtable@C@:
0 | 0
1 | 8 (Dd(C+0)A)
vbi: class offset o.vbptr o.vbte fVtorDisp
A 16 0 4 0