✨前言✨
🎓作者:【 教主 】
📜文章推荐:
☕博主水平有限,如有错误,恳请斧正。
📌机会总是留给有准备的人,越努力,越幸运!
💦导航助手💦
什么是继承?
继承是面向对象编程的三大特性之一,是一种使代码得以复用的重要手段。使程序员可以在已有类的基础上进行拓展,产生新的类,已有类称为基类或父类,新的类称为派生类或子类。
class Person
{
public:
int age;
string name;
};
class Student:public Person
{
public:
int stuId;
};
继承的书写格式:class 子类:继承方式 父类
继承方式
- public继承
- protected继承
- private继承
不同的继承方式在子类中的基类成员访问方式不同
总结:
- 无论是哪种继承方式,基类中的私有成员在子类中都是不可以访问的
- 继承方式可以省略,class默认继承是private,struct默认继承是public,但是为了代码的可读性,建议写上继承方式
- 一般都用public继承,很少用其他两种方式继承
继承中的一些问题
切片
将子类对象赋值给父类对象/父类指针/父类引用就叫做切片,顾名思义,就是把子类中继承自父类的那一部分切出来赋值。
赋值完成后父类对象/父类指针/父类引用就只保留属于父类的那一部分
注意:子类可以赋值给父类,但是父类不能赋值给子类
继承的隐藏规则
规则:如果父类与子类定义了同名变量或者同名函数,那么子类在访问该变量或者函数时默认访问的是子类的,而不是父类的,如果要访问子类对象中继承来自父类的同名变量或者同名函数需要指定父类作用域。
举例:类A有成员变量num,成员函数print(),类B继承类A,类B定义同名变量num,同名函数print()
代码如下:
class A
{
public:
int num;
void print()
{
cout << "A::print()" << endl;
}
};
class B :public A
{
public:
int num;
void print()
{
cout << "B::print()" << endl;
}
};
int main()
{
B b;
//访问b中的变量num与print()
b.num = 10;
cout << b.num << endl;
b.print();
//访问b中继承自A的变量num与print()
b.A::num = 20;
cout << b.A::num << endl;
b.A::print();
return 0;
}
代码运行结果如下:
注意:这不是函数重载!函数重载是在同一个作用域内函数名相同而参数不同,这是隐藏,是两个作用域。
默认成员函数
- 子类构造函数必须调用父类构造函数来初始化继承自父类的部分,如果父类没有默认构造,必须在子类构造函数初始化列表中显式调用父类构造来初始化
- 拷贝构造与赋值重载也必须调用父类的来完成
- 析构函数中子类完成清理工作后会自动调用父类析构,不用显式调用父类析构
构造与析构顺序
构造:父类先构造,子类后构造
析构:子类先析构,父类后析构
举例代码如下:
class A
{
public:
A()
{
cout << "A()" << endl;
}
~A()
{
cout << "~A()" << endl;
}
};
class B :public A
{
public:
B()
{
cout << "B()" << endl;
}
~B()
{
cout << "~B()" << endl;
}
};
int main()
{
B b;
return 0;
}
运行结果如下:
静态成员
整个继承体系中只有一个static成员,无论怎样继承只有一份。
菱形继承
什么是菱形继承?
菱形继承,也叫做钻石继承,因为形状像菱形,所以叫做菱形继承。
class A
{
public:
int a;
};
class B1 :public A
{};
class B2 :public A
{};
class C :public B1, B2
{};
有一类A,A中有一个数据成员a,类B1和类B2公有继承类A,所以B1和B2中都有数据成员a,类C公有继承B1和B2,这时候类C中就有两份数据成员a,一份来自B1,一份来自B2,这就造成了数据冗余。并且在访问C对象访问a时会造成二义性。
如何解决菱形继承?
解决菱形继承需要使用虚继承。B1和B2虚继承A,继承时加上关键字virtual,代码如下。
class A
{
public:
int a;
};
class B1 :virtual public A
{};
class B2 :virtual public A
{};
class C :public B1, B2
{};
这样即可解决菱形继承的代码冗余以及二义性问题。无论是指定作用域访问还是默认访问,访问的都是同一个变量a。
可能有人问:为什么C++要设计这么复杂的继承机制,不像java一样,不支持多继承不就解决这样复杂的问题了吗?
在我的博客:深度剖析菱形继承 中,深度解析了菱形继承的细节问题,感兴趣的朋友可以自行阅读。
C++作为比较早期的面向对象的编程语言,属于是摸着石头过河,所以有缺陷也是可以理解的。
欢迎关注,码字不易,希望多多点赞、收藏哦!抱拳了。