1. 继承的基本语法
继承的好处:减少重复的代码
class 子类 : 继承方式 父类
2. 继承的方式
先定义一个父类:
// 公共继承
// 父类/基类
class Base
{
public:
int a;
protected:
int b;
private:
int c;
}
2.1 公共继承
子类继承父类中除了私有属性的全部属性,然后继承来的属性权限和父类相同。
// 子类/派生类
class Derived_Public : public Base
{
// 子类的继承
public:
int a;
protected:
int b;
}
2.2 保护继承
子类继承父类中除了私有属性的全部属性,然后继承来的属性权限改为保护权限。
class Derived_Protected : protected Base
{
// 子类的继承
protected:
int a;
int b;
}
2.3 私有继承
子类继承父类中除了私有属性的全部属性,然后继承来的属性权限改为私有权限。
class Derived_Private : private Base
{
// 子类的继承
private:
int a;
int b;
3.继承中的构造和析构
子类继承父类后,当子类创建对象,直接调用父类的构造函数。
父类先构造,后析构;
子类后构造,先析构。
继承构造函数时,格式为 Derived(带数据类型的参数列表) : Base(不带数据类型的参数列表) { /* ...*/ }
在C++11之后,使用继承构造函数,即使用关键字using: using Base::Base;
注意:
- 使用继承构造函数来初始化子类时,只能初始化从父类中继承来的成员属性,子类中额外的成员变量无法通过这个方式初始化;
- 对于子类中新增的成员属性可以通过定义子类的构造函数来实现初始化;
- 多继承的情况下,继承构造函数会出现“冲突”的情况,因为多个基类中的部分构造函数可能导致派生类中的继承构造函数的函数名与参数相同,即函数签名;为避免继承构造函数冲突,可以通过显式定义来阻止隐式生成的继承构造函数。
更多细节可以参照这篇文章。-> c++11 继承构造函数和委托构造函数
4. 继承中同名成员的处理方式
4.1 同名函数的处理
当子类中出现与父类中同名的函数时:
子类成员函数直接调用;
父类成员函数加作用域。
class Base
{
public:
int mA;
int mB;
Base(int a, int b) : mA(a), mB(b) { }
int Add()
{
return mA + mB;
}
}
class Derived : public Base
{
int mC;
int mD;
int Add()
{
return mC + mD;
}
}
void test_1()
{
Derived P2;
}
4.2 同名属性处理
当子类中出现与父类中同名的属性时:
子类成员属性直接调用;
父类成员属性加作用域。
5.继承静态同名成员
类似于继承非非静态成员时的处理方式。
5.1 同名的静态成员属性
除了和非静态成员属性用同一种方法——通过对象访问外,还可以通过类名的方式访问:
即,使用 Derived :: 静态成员属性名
;
如果想通过该种方式访问父类作用域下的静态成员变量则可以使用 Derived :: Base :: 静态成员属性名
。
5.2 同名的静态成员函数
类似于访问成员属性。
by the way, 静态成员忘记了可以去这边复习->C++学习日记——对象的初始化和清理
6. 多继承语法
语法:class Derived : 继承方式1 Base1, 继承方式2 Base2, ...
值得注意的是,多继承可能会引起继承同名成员的问题,这时候应当使用作用于加以区分。
另外,在实际开发中,不建议使用多继承。
7. 菱形继承
7.1 菱形继承的产生
7.2 产生的问题
- 父类中,动物中的相同属性,骡子继承了两次,占用内存空间;
- 继承之后,访问时会出现二义性。
7.3 解决方法
- 利用虚继承来解决菱形继承的问题,即,在继承方式前面加上关键字 virtual 变为虚继承,如:
class Animal { public: int age; }; class Donkey : virtual public Animal {}; class Horse : virtual public Animal {}; class mule : public Donkey, public Horse {};
此时,Animal称为虚基类;当使用这种方式继承时,mule保存的是两个父类的存储指针(好像叫虚基类指针?vbptr)然后是通过计算偏移量来找到存储的位置。这个可以通过
开发人员命令提示符
来查看。
- 访问时,可以通过加作用域来分别访问。
8. 继承的注意事项
- 父类中的私有属性,子类无论通过那种方式都不可以继承;
- 在多次继承中,权限只会越来越少;
- 继承时,父类中所有非静态成员属性都会被子类所继承,只不过父类中的私有属性被编译器给隐藏了,访问不到而已;
- 使用VS2022的
开发人员命令提示符
,路径移动到cpp代码文件的文件夹下,使用命令可以打印出某个类的继承关系图,命令如下
cl /d1 reportSingleClassLayout类名 "cpp文件名"
如:
代码如下:class Person // 父类 { public: int age; private: int sex; }; class Student :public Person // 子类 { public: int num; };
- 如果子类中出现和父类同名的成员函数,子类的同名成员会覆盖掉父类中所有的成员函数(包括同名的重载函数),加作用域后可以访问。
9. 放松时刻