【C++】你不得不爱的——继承(二)

简介: 【C++】你不得不爱的——继承

2.子类先析构,父类再析构。子类析构函数不需要显示调用父类析构,子类析构后会自动调用父类析构

构造顺序:先父类,再子类;析构顺序:先子类,再父类。

5.继承与友元

友元关系不能继承!

若子类对象也想访问友元函数,那只能在子类中也加上友元!(但不建议使用友元,会破坏继承关系)


6. 继承与静态成员

子类继承父类,不是继承父类这个对象,而是会有一份父类的模型。父类有的成员变量,子类也会有一份,互不干扰。


但静态成员就不一样了,他们是同一份;静态成员属于整个类和类的所有对象。同时也属于所有派生类及派生类的对象。

class Person
{
public:
  //friend void Display(const Person& p, const Student& s);
protected:
  string _name; // 姓名
public:
  static int _num;
};
int Person::_num = 0;
class Student : public Person
{
protected:
  int _stuNum; // 学号
};
void test()
{
  Student s;
  Person p;
  cout << p._num << endl;
  p._num++;
  cout << p._num << endl;
  s._num++;
  cout << s._num << endl;
  cout << &s._num << endl;
  cout << &p._num << endl;
}

基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子

类,都只有一个static成员实例

重点:

  Person* ptr = nullptr;
  ptr->Print();
  cout << ptr->_num << endl;
  cout << ptr->_name << endl;
  cout << (*ptr)._num << endl;
  (*ptr).Print();

对象里面只存成员变量,成员函数在代码段中,所以以上代码哪个不对呢?


我们知道空指针不能解引用,解引用意思是,这里是去访问指针指向对象的内部成员,那看一看哪个访问了内部的成员呢?


函数不在内部,在代码段,可以!


_num为对象内部成员变量,不能解引用访问,不可以!


(*ptr)是解引用了吗?我们不能凭借解引用符号来判断是否解引用,我们需要看内部的访问情况,(*ptr)->Print();并没有访问内部成员,可以!


(*ptr)->_num;也可以,_num是静态成员,不在成员里面。

7.多继承

7.1继承分类

单继承:一个子类只有一个直接父类

多继承:一个子类有两个或两个以上的父类

菱形继承:是多继承的一种特殊情况,会产生数据冗余和二义性!

(person类的中的成员,会在student和teacher中都有一份,assistant继承student和teacher时,assistant中会有两份person,造成了数据冗余和二义性)

解决方法:

解决二义性:

可以通过访问限定符来指定访问哪一个成员。

那如何解决二义性的问题呢?

此时虚继承就上线了!

虚继承在腰部继承,谁引发的数据冗余,谁就进行虚继承(解决冗余)

由此可见,加上virtual,变为虚继承以后,确实解决了数据的冗余


那么到底如何解决的呢??具体下面分析!


7.2 菱形继承 &&菱形虚拟继承

为了研究虚拟继承原理,我们给出了一个简化的菱形继承继承体系,再借助 内存窗口观察对象成

员的模型。

1.解决二义性的过程(指定作用域)

(菱形继承)

class A
{
public:
  int _a;
};
class B:public A
{
public:
  int _b;
};
class C:public A
{
public:
  int _c;
};
class D:public B,public C
{
public:
  int _d;
};
int main()
{
  D d;
  d._b = 1;
  d._c = 2;
  d._d = 3;
  d.B::_a = 4;
  d.C::_a = 5;
}

2.解决数据冗余(需要虚继承)

(菱形虚拟继承)

class A
{
public:
  int _a;
};
class B:virtual public A
{
public:
  int _b;
};
class C:virtual public A
{
public:
  int _c;
};
class D:public B,public C
{
public:
  int _d;
};
int main()
{
  D d;
  d._b = 1;
  d._c = 2;
  d._d = 3;
  d.B::_a = 4;
  d.C::_a = 5;
}

那如果遇到这种情况呢???父子类的赋值转换(切片)

class A
{
public:
  int _a;
};
class B:virtual public A
{
public:
  int _b;
};
class C:virtual public A
{
public:
  int _c;
};
class D:public B,public C
{
public:
  int _d;
};
int main()
{
  D d;
  d._b = 1;
  d._c = 2;
  d._d = 3;
  d._a = 4;
  d._a = 5;
  B b;
  b._a = 1;
  b._b = 3;
  B* ptr = &b;
  ptr->_a = 2;
  ptr = &d;
  ptr->_a = 6;
}

从b对象可以看的出来,只要是虚继承以后,就会把虚基类放到最下面;


就像切片这种情况,ptr指向不同,那么距离虚基类的距离就不同,所以就必须要有虚基表的地址,来访问虚基表继而找到偏移量,然后访问到虚基类!


我们通常使用下,很忌讳出现菱形继承,但可以多继承。


可以看得出,虚继承在时间上确实有损耗,过程比较复杂,但是如果虚基类比较大时,就可以很大程度上节省内存。


8.继承和组合(都是一种复用)

public继承是一种 is-a(是一个)的关系。也就是说每个派生类对象都是一个基类对象。

组合是一种 has-a(有一个)的关系。假设B组合了A,每个B对象中都有一个A对象。

我们会说低耦合高内聚有,意思就是相互的联系比较小,不会因为改动一个,而很大的影响另一个;

在组合中,两个类中的成员变量一般都是私有,那么就无法访问,那么修改也不会相互影响到;

在继承中,因为要继承,所以父类成员一般子类都可以访问的,那么修改的话,彼此相互影响就比较大!

那么组合其实就是很好的低耦合。

就比如我们平时举例说到的:person,student,这就是继承关系,学生是一个人;

那再举一个,头有一双眼睛(这就是组合)

事实上,哪个适合就用哪个,都适合就先用组合!

总结:

一口气说了这么多,你学会了吗?细节还是比较多的,我们应该下去多多自己琢磨,反复调试,去感受过程,从而理解的更深刻!下期再见!

目录
相关文章
|
1月前
|
C++ 开发者
C++学习之继承
通过继承,C++可以实现代码重用、扩展类的功能并支持多态性。理解继承的类型、重写与重载、多重继承及其相关问题,对于掌握C++面向对象编程至关重要。希望本文能为您的C++学习和开发提供实用的指导。
54 16
|
30天前
|
编译器 数据安全/隐私保护 C++
【C++面向对象——继承与派生】派生类的应用(头歌实践教学平台习题)【合集】
本实验旨在学习类的继承关系、不同继承方式下的访问控制及利用虚基类解决二义性问题。主要内容包括: 1. **类的继承关系基础概念**:介绍继承的定义及声明派生类的语法。 2. **不同继承方式下对基类成员的访问控制**:详细说明`public`、`private`和`protected`继承方式对基类成员的访问权限影响。 3. **利用虚基类解决二义性问题**:解释多继承中可能出现的二义性及其解决方案——虚基类。 实验任务要求从`people`类派生出`student`、`teacher`、`graduate`和`TA`类,添加特定属性并测试这些类的功能。最终通过创建教师和助教实例,验证代码
48 5
|
3月前
|
编译器 C++ 开发者
【C++】继承
C++中的继承是面向对象编程的核心特性之一,允许派生类继承基类的属性和方法,实现代码复用和类的层次结构。继承有三种类型:公有、私有和受保护继承,每种类型决定了派生类如何访问基类成员。此外,继承还涉及构造函数、析构函数、拷贝构造函数和赋值运算符的调用规则,以及解决多继承带来的二义性和数据冗余问题的虚拟继承。在设计类时,应谨慎选择继承和组合,以降低耦合度并提高代码的可维护性。
42 1
【C++】继承
|
7月前
|
编译器 C++
【C++】详解C++的继承
【C++】详解C++的继承
|
4月前
|
安全 程序员 编译器
【C++篇】继承之韵:解构编程奥义,领略面向对象的至高法则
【C++篇】继承之韵:解构编程奥义,领略面向对象的至高法则
112 11
|
4月前
|
C++
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
72 1
|
4月前
|
C++
C++番外篇——虚拟继承解决数据冗余和二义性的原理
C++番外篇——虚拟继承解决数据冗余和二义性的原理
63 1
|
4月前
|
安全 编译器 程序员
C++的忠实粉丝-继承的热情(1)
C++的忠实粉丝-继承的热情(1)
33 0
|
4月前
|
编译器 C++
C++入门11——详解C++继承(菱形继承与虚拟继承)-2
C++入门11——详解C++继承(菱形继承与虚拟继承)-2
55 0
|
4月前
|
程序员 C++
C++入门11——详解C++继承(菱形继承与虚拟继承)-1
C++入门11——详解C++继承(菱形继承与虚拟继承)-1
60 0