06_继承与派生

eokey eokey     2023-05-04     533

关键词:

一:继承的概念

  面向对象程序设计有 4 个主要特点:抽象、封装、继承和多态性。我们已经讲解了类和对象,了解了面向对象程序设计的两个重要特征一数据抽象与封装,已经能够设计出基于对象的程序,这是面向对象程序设计的基础。
  要较好地进行面向对象程序设计,还必须了解面向对象程序设计另外两个重要特征——继承性和多态性。继承性是面向对象程序设计最重要的特征,可以说,如果没有掌握继承性,就等于没有掌握类和对象的精华,就是没有掌握面向对象程序设计的真谛。

(1)类之间的关系

has-A, uses-A 和 is-A
has-A 包含关系,用以描述一个类由多个“部件类”构成。实现 has-A 关系用类成员表示,即一个类中的数据成员是另一种已经定义的类。
uses-A 一个类部分地使用另一个类。通过类之间成员函数的相互联系,定义友员或对象参数传递实现。
is-A 机制称为“继承”。关系具有传递性,不具有对称性。

(2)继承关系举例
万事万物中皆有继承,是重要的现象
两个案例: 1)植物继承图; 2)程序员继承图
技术图片

 

 

 (3)继承相关概念

技术图片

 

 

 (4)派生类的定义

技术图片

 

 

 (5)继承的重要说明

1、子类拥有父类的所有成员变量和成员函数
2、子类可以拥有父类没有的方法和属性
3、子类就是一种特殊的父类
4、子类对象可以当作父类对象使用

二:派生类的访问控制
  派生类继承了基类的全部成员变量和成员方法(除了构造和析构之外的成员方法),但是这些成员的访问属性,在派生过程中是可以调整的。

(1)单个类的访问控制
1、类成员访问级别(publicprivateprotected
2、思考:类成员的访问级别只有 public private 是否足够?

(2)不同的继承方式会改变继承成员的访问属性
1) C++中的继承方式会影响子类的对外访问属性
  public 继承:父类成员在子类中保持原有访问级别
  private 继承:父类成员在子类中变为 private 成员
  protected 继承:父类中 public 成员会变成 protected
    父类中 protected 成员仍然为 protected
    父类中 private 成员仍然为 private

2) private 成员在子类中依然存在,但是却无法访问到。不论种方式继承基类,派生类都不能直接使用基类的私有成员 。
3) C++中子类对外访问属性表

技术图片

 

 

 4)继承中的访问控制
技术图片

 

 

 (3)“三看”原则
C++中的继承方式(public、 private、 protected)会影响子类的对外访问属性
判断某一句话,能否被访问
1)看调用语句,这句话写在子类的内部、外部
2)看子类如何从父类继承(public、 private、 protected)
3)看父类中的访问级别(public、 private、 protected)

(4)派生类类成员访问级别设置的原则
思考:如何恰当的使用 public, protected 和 private 为成员声明访问级别?
1、需要被外界访问的成员直接设置为 public
2、只能在当前类中访问的成员设置为 private
3、只能在当前类和子类中访问的成员设置为 protected, protected 成员的访问权限介于public 和 private 之间。

(5)综合训练
练习:
public 继承不会改变父类对外访问属性;
private 继承会改变父类对外访问属性为 private;
protected 继承会部分改变父类对外访问属性。
结论:一般情况下 class B : public A

//类的继承方式对子类对外访问属性影响
#include <cstdlib>
#include <iostream>
using namespace std;
class A

private:
  int a;
protected:
  int b;
public:
  int c;
  A()
  
    a = 0;
    b = 0;
    c = 0;
  
  void set(int a, int b, int c)
  
    this->a = a;
    this->b = b;
    this->c = c;
  
;
class B : public A

public:
  void print()
  
    //cout<<"a = "<<a; //err
    cout<<"b = "<<b;
    cout<<"c = "<<endl;
  
;
class C : protected A

public:
  void print()
  
    //cout<<"a = "<<a; //err
    cout<<"b = "<<b;
    cout<<"c = "<<endl;
  
;
class D : private A

public:
  void print()
  
    //cout<<"a = "<<a; //err
    cout<<"b = "<<b<<endl;
    cout<<"c = "<<c<<endl;
  
;
int main_01(int argc, char *argv[])

  A aa;
  B bb;
  C cc;
  D dd;
  aa.c = 100; //ok
  bb.c = 100; //ok
  //cc.c = 100; //err 类的外部是什么含义
  //dd.c = 100; //err
  aa.set(1, 2, 3);
  bb.set(10, 20, 30);
  //cc.set(40, 50, 60); //ee
  //dd.set(70, 80, 90); //ee
  bb.print();
  cc.print();
  dd.print();
  system("pause");
  return 0;

三:继承中的构造和析构
(1)类型兼容性原则
  类型兼容规则是指在需要基类对象的任何地方,都可以使用公有派生类的对象来替代。通过公有继承,派生类得到了基类中除构造函数、析构函数之外的所有成员。这样,公有派生类实际就具备了基类的所有功能,凡是基类能解决的问题,公有派生类都可以解决。类型兼容规则中所指的替代包括以下情况:
  子类对象可以当作父类对象使用
  子类对象可以直接赋值给父类对象
  子类对象可以直接初始化父类对象
  父类指针可以直接指向子类对象
  父类引用可以直接引用子类对象
在替代之后,派生类对象就可以作为基类的对象使用,但是只能使用从基类继承的成员。类型兼容规则是多态性的重要基础之一。

总结: 子类就是特殊的父类 (base *p = &child;)
#include <cstdlib>
#include <iostream>
using namespace std;
/*
子类对象可以当作父类对象使用
子类对象可以直接赋值给父类对象
子类对象可以直接初始化父类对象
父类指针可以直接指向子类对象
父类引用可以直接引用子类对象
*/
//子类就是特殊的父类
class Parent03

protected:
  const char* name;
public:
  Parent03()
  
    name = "Parent03";
  
  void print()
  
    cout<<"Name: "<<name<<endl;
  
;
class Child03 : public Parent03

protected:
  int i;
public:
  Child03(int i)
  
    this->name = "Child2";
    this->i = i;
  
;
int main()

  Child03 child03(1000);
  //分别定义父类对象 父类指针 父类引用 child
  Parent03 parent = child03;
  Parent03* pp = &child03;
  Parent03& rp = child03;
  parent.print();
  pp->print();
  rp.print();
  system("pause");
  return 0;

(2)继承中的对象模型
  类在 C++编译器的内部可以理解为结构体
  子类是由父类成员叠加子类新成员得到的

技术图片

 

 

 技术图片

 

 

 继承中构造和析构

问题: 如何初始化父类成员?父类与子类的构造函数有什么关系
在子类对象构造时,需要调用父类构造函数对其继承得来的成员进行初始化
在子类对象析构时,需要调用父类析构函数对其继承得来的成员进行清理
#include <cstdlib>
#include <iostream>
using namespace std;
class Parent04

public:
  Parent04(const char* s)
  
    cout<<"Parent04()"<<" "<<s<<endl;
  
  ~Parent04()
  
    cout<<"~Parent04()"<<endl;
  
;
class Child04 : public Parent04

public:
  Child04() : Parent04("Parameter from Child!")
  
    cout<<"Child04()"<<endl;
  
  ~Child04()
  
    cout<<"~Child04()"<<endl;
  
;

void run04()

  Child04 child;

int main_04(int argc, char *argv[])

  run04();
  system("pause");
  return 0;

(3)继承中的构造析构调用原则
1、子类对象在创建时会首先调用父类的构造函数
2、父类构造函数执行结束后,执行子类的构造函数
3、当父类的构造函数有参数时,需要在子类的初始化列表中显示调用
4、析构函数调用的先后顺序与构造函数相反

(4)继承与组合混搭情况下,构造和析构调用原则

原则: 先构造父类,再构造成员变量、最后构造自己
先析构自己,在析构成员变量、最后析构父类
//先构造的对象,后释放
//子类对象如何初始化父类成员
//继承中的构造和析构
//继承和组合混搭情况下,构造函数、析构函数调用顺序研究
#include <iostream>
using namespace std;
class Object

public:
  Object(const char* s)
  
    cout<<"Object()"<<" "<<s<<endl;
  
  ~Object()
  
    cout<<"~Object()"<<endl;
  
;
class Parent : public Object

public:
  Parent(const char* s) : Object(s)
  
    cout<<"Parent()"<<" "<<s<<endl;
  
  ~Parent()
  
    cout<<"~Parent()"<<endl;
  
;
class Child : public Parent

protected:
  Object o1;
  Object o2;
public:
  Child() : o2("o2"), o1("o1"), Parent("Parameter from Child!")
  
    cout<<"Child()"<<endl;
  
  ~Child()
  
    cout<<"~Child()"<<endl;
  
;
void run05()

  Child child;

int main05(int argc, char *argv[])

  cout<<"demo05_extend_construct_destory.cpp"<<endl;
  run05();
  system("pause");
  return 0;

(5)继承中的同名成员变量处理方法
1、当子类成员变量与父类成员变量同名时
2、子类依然从父类继承同名成员
3、在子类中通过作用域分辨符::进行同名成员区分(在派生类中使用基类的同名成员,显式地使用类名限定符)
4、同名成员存储在内存中的不同位置

技术图片

 

 

 技术图片

 

 

 总结:同名成员变量和成员函数通过作用域分辨符进行区分

(6)派生类中的 static 关键字
继承和 static 关键字在一起会产生什么现象哪?
理论知识
  ? 基类定义的静态成员,将被所有派生类共享
  ? 根据静态成员自身的访问特性和派生类的继承方式,在类层次体系中具有不同的访问性质 (遵守派生类的访问控制)

  ? 派生类中访问静态成员,用以下形式显式说明:

类名 :: 成员
或通过对象访问 对象名 . 成员

 技术图片

 

 

 技术图片

 

 

 技术图片

 

 

 总结:
1> static 函数也遵守 3 个访问原则
2> static 易犯错误(不但要初始化,更重要的显示的告诉编译器分配内存)
3> 构造函数默认为 private

四:多继承

(1)多继承应用

多继承概念
? 一个类有多个直接基类的继承关系称为多继承
? 多继承声明语法
class 派生类名 : 访问控制 基类名 1 , 访问控制 基类名 2 , … , 访问控制 基类名 n

   数据成员和成员函数声明

? 类 C 可以根据访问控制同时继承类 A 和类 B 的成员,并添加自己的成员

技术图片

 

 

 多继承的派生类构造和访问
? 多个基类的派生类构造函数可以用初始式调用基类构造函数初始化数据成员
? 执行顺序与单继承构造函数情况类似。多个直接基类构造函数执行顺序取决于定义派生类时指定的各个继承基类的顺序。
? 一个派生类对象拥有多个直接或间接基类的成员。不同名成员访问不会出现二义性。如果不同的基类有同名成员,派生类对象访问时应该加以识别。

多继承简单应用
技术图片

 

 

 技术图片

 

 

 (2)虚继承

如果一个派生类从多个基类派生,而这些基类又有一个共同的基类,则在对该基类中声明的名字进行访问时,可能产生二义性。

技术图片

 

 技术图片

 

 总结:
? 如果一个派生类从多个基类派生,而这些基类又有一个共同的基类,则在对该基类中声明的名字进行访问时,可能产生二义性
? 如果在多条继承路径上有一个公共的基类,那么在继承路径的某处汇合点,这个公共基类就会在派生类的对象中产生多个基类子对象
? 要使这个公共基类在派生类中只产生一个子对象,必须对这个基类声明为虚继承,使这个基类成为虚基类。
? 虚继承声明使用关键字 virtual

技术图片

 

 技术图片

 

 五:继承总结
? 继承是面向对象程序设计实现软件重用的重要方法。程序员可以在已有基类的基础上定义新的派生类。
? 单继承的派生类只有一个基类。多继承的派生类有多个基类。
? 派生类对基类成员的访问由继承方式和成员性质决定。
? 创建派生类对象时,先调用基类构造函数初始化派生类中的基类成员。调用析构函数的次序和调用构造函数的次序相反。
? C++提供虚继承机制,防止类继承关系中成员访问的二义性。
? 多继承提供了软件重用的强大功能,也增加了程序的复杂性。

面向对象之继承与派生(代码片段)

一、继承  1、含义:继承指的是类与类之间的关系,是一种什么“是”什么的关系,继承的功能之一就是用来解决代码重用问题。  2、特点:继承是一种创建新类的方式,在Python中,新建的类可以继承一个或多个父类,而... 查看详情

继承与派生(代码片段)

一、继承1.什么是继承继承是一种新建类的方式,新建的类称之为子类或派生类,继承的父类称之为基类或超类。在python中,一个子类可以继承多个父类在其他语言中,一个子类只能继承一个父类2.继承的作用减少代码的冗余3.继... 查看详情

面向对象——继承派生组合以及接口

一、继承与派生1.1什么是继承继承是一种创建新类的方式,在Python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类Python中类的继承分为:单继承和多继承classPeople:#定义父类def__init__... 查看详情

绑定与非绑定方法继承继承与抽象查找属性关系派生与覆盖访问父类的内容

类属性与对象属性类中应该仅存储所有对象共有的内容 如所有人的国籍相同那就放到类中对象中存储每个对象独有的内容?如每个人的名字都不同初始化函数classPerson:color=‘white‘defread(self):print(‘hamlet‘)#创建对象也称之为实... 查看详情

面向对象的继承和派生(代码片段)

一、继承1、什么是继承继承是一种创建新类的方式,新建的类可以继承一个或多个父类(python支持多继承),父类又可称为基类或超类,新建的类称为派生类或子类。子类会“”遗传”父类的属性,从而解决代码重用... 查看详情

c++的探索路13继承与派生之练习篇(需重新学习)(代码片段)

由程序结果填空输出:4,6填空:classA intval;public: A(intn) val=n; intGetVal() returnval; ;classB:publicAprivate: intval;public: B(intn):____ intGetVal() returnval; ;intmain() Bb1(2); 查看详情

c++___继承(代码片段)

文章目录1.继承的概念2.继承的定义格式2.1继承关系和访问限定符3.基类和派生类对象赋值转换4.继承中的作用域5.派生类的默认成员函数6.继承与友元7.继承与静态成员8.复杂的菱形继承及菱形虚拟继承(重点)9.继承的总... 查看详情

c++_继承详谈(代码片段)

文章目录1.继承的概念及定义1.1继承的概念1.2代码示例1.3继承定义1.3.1定义格式1.3.2继承关系和访问限定符1.3.3继承基类成员访问方式2.基类和派生类对象赋值转换2.1代码示例3.继承中的作用域3.1代码示例4.派生类的默认成员函数4.1... 查看详情

继承||派生||继承实现原理(代码片段)

继承继承:是类与类之间的关系classParentClass1:#父类,基类,超生类passclassParentClasss2:passclassSubClass1(ParentClass1):#子类,派生类passclassSubClass2(ParentClass1,ParentClasss2):pass#print(SubClass1.__bases__)#查看所继承的父类#print(S 查看详情

3.继承与派生

1.类的继承与派生  -类的继承:从已有类产生新类的过程。原有类称为基类或父类,产生的新类称为派生类或子类。  -派生类语法:      class派生类名:继承方式 基类名1,继承方式  基类名2,...    { ... 查看详情

继承,派生,组合

1.继承继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类1classPeople:2pass3classAnimal:4pass5classStudent(People,Animal):#people,Animal是父类(基类),Student是子类(派... 查看详情

继承与派生

...似软件元素的过程。C++强调软件的可重用性,并且提供了继承机制来解决软件的可重用性问题。4.2 单继承4.2.1单继承的定义方式定义派生类的语法形式为:class派生类名:继承方式基类名派生类中的新增成员;继承方式也称访... 查看详情

类的继承与派生(代码片段)

第5章类的继承与派生1类的继承与类的派生继承人们追求代码复用(这是提高软件开发效率的重要手段),将继承和派生用于程序设计方法中,从而有了面向对象程序设计的重要特点。C++对代码复用有很强的支持,继承就是支持代... 查看详情

5继承与派生2-访问控制

 1、公有继承当类的继承方式为公有继承时,基类的公有和保护成员的访问属性在派生类中不变,而基类的私有成员不可直接访问。---也就是说基类的公有成员和保护成员被继承到派生类中访问属性不变,仍作为派生类的公... 查看详情

大型程序的工具——多重继承与虚继承(代码片段)

一、多重继承与虚继承1、多重继承  在派生类的派生列表中可以包含多个基类,每个基类包含一个可选的访问说明符。  多重继承的派生列表也只能包含已经定义过的类,而且这些类不能是final的。对于派生类能够继承的基... 查看详情

继承与派生(代码片段)

1、继承&派生在定义一个新的类B时,若该类与某个已有的类A相似(B拥有A的全部特点),则可以将A作为一个基类,把B作为基类的一个派生类(子类)。在派生类的各个成员函数中,不能访问基类中的private成员。class派生类名... 查看详情

继承与派生(代码片段)

什么是继承 继承是一种创建新类的方式,新建的类可以继承一个或多个父类(python支持多继承),父类又可称为基类或超类,新建的类称为派生类或子类。python中类的继承分为:单继承和多继承classParentClass1:#定义父类passclas... 查看详情

继承与派生(代码片段)

一、初识继承1,什么是继承?继承是一种创建新类的方式,新建的类可以继承一个或多个父类(python支持多继承),父类又可称为基类或超类,新建的类称为派生类或子类。子类会“遗传”父类的属性,从而解决代码重用... 查看详情