06_继承与派生

一:继承的概念

  面向对象程序设计有 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、类成员访问级别(public、 private、 protected)
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++提供虚继承机制,防止类继承关系中成员访问的二义性。
? 多继承提供了软件重用的强大功能,也增加了程序的复杂性。

原文地址:https://www.cnblogs.com/eokey/p/12038483.html

时间: 2024-11-09 00:50:15

06_继承与派生的相关文章

程序设计实习MOOC / 继承和派生——编程作业 第五周程序填空题1

描述 写一个MyString 类,使得下面程序的输出结果是: 1. abcd-efgh-abcd- 2. abcd- 3. 4. abcd-efgh- 5. efgh- 6. c 7. abcd- 8. ijAl- 9. ijAl-mnop 10. qrst-abcd- 11. abcd-qrst-abcd- uvw xyz about big me take abcd qrst-abcd- 要 求:MyString类必须是从C++的标准类string类派生而来.提示1:如果将程序中所有 "My

C++继承与派生

2017-06-25 23:00:59 c++中的继承和派生是面向对象编程中的一个重要内容,通过继承可以实现代码的复用,同时继承也是实现多态性的基础. 一.c++继承的基本形式 class 派生类名:继承方式 基类名,继承方式 基类名 {}: 继承方式主要有三种,public ,private ,protected. 缺省条件下是private继承,三种中public继承用的最多,不同的继承方式决定了子类中从基类继承过来的成员的访问属性. public继承: 基类的public,protecte

四.OC基础--1.文档安装和方法重载,2.self和super&amp;static,3.继承和派生,4.实例变量修饰符 ,5.私有变量&amp;私有方法,6.description方法

四.OC基础--1.文档安装和方法重载, 1. 在线安装 xcode-> 系统偏好设置->DownLoads->Doucument->下载 2. 离线安装 百度xcode文档 3. 方法重载: 是指在一个类中定义多个同名的方法 在OC中没有重载 2.self和super&static, self和super: 1. self理解: 谁调用当前方法, self就代表谁. 比如: 在对象方法中,self代表的是对象, 因为只有对象才可以调用对象方法 在类方法中, self代表的

模块的封装之C语言类的继承和派生

[交流][微知识]模块的封装(二):C语言的继承和派生 在模块的封装(一):C语言的封装中,我们介绍了如何使用C语言的结构体来实现一个类的封装,并通过掩码结构体的方式实 现了类成员的保护.这一部分,我们将 在此的基础上介绍C语言类的继承和派生.其实继承和派生是一个动作的两种不同角度的表达 .当我们继承了一个基类而创造了一个新类时,派生的概念就诞生了.派生当然是从基类派生的.派生出来的类当然是继承了基类的 东西.继承和派生不是一对好基友,他们根本就是一个动作的两种不同的说法,强调动作的起始点的时候

3.继承与派生

1.类的继承与派生 - 类的继承:从已有类产生新类的过程.原有类称为基类或父类,产生的新类称为派生类或子类. - 派生类语法: class 派生类名:继承方式   基类名1,继承方式 基类名2,... { } - 单继承和多继承:基类个数决定 - 直接基类,间接基类 - 继承方式规定了如何访问从基类继承的成员 - 派生类成员是指除了从基类继承的所有成员之外,新增加的数据和函数成员 - 派生类生成过程:吸收基类成员->改造基类成员->添加新的成员,构造函数和析构函数都不被继承 2.访问控制 -

嵌入式linux C++语言(七)——继承与派生

嵌入式linux C++语言(七)--继承与派生 一.继承 在C++编程中软件可重用性(software reusability)是通过继承(inheritance)机制来实现的.类的继承,是新的类从已有类那里得到已有的特性.或从已有类产生新类的过程就是类的派生.原有的类称为基类或父类,产生的新类称为派生类或子类. 派生类的声明:class 派生类名:[继承方式] 基类名{派生类成员声明:};    一个派生类可以同时有多个基类,这种情况称为多重继承,派生类只有一个基类,称为单继承. 继承方式规

继承与派生,重载&lt;&lt;

///继承与派生 #include <iostream> using namespace std; class Point { public: Point (float a=0,float b=0):x(a),y(b) {} ///有默认参数的构造函数 void setPoint (float,float); ///重新设置坐标 ///读x的坐标,getX为常成员函数,只能访问类的数据,而不能更改 float getX() const { return x; } float getY() co

c++学习--继承与派生

继承和派生 1 含有对象成员(子对象)的派生类的构造函数,定义派生类对象成员时,构造函数的执行顺序如下: 1 调用基类的构造函数,对基类数据成员初始化: 2 调用对象成员的构造函数,对对象成员的数据成员初始化: 3 执行派生类的构造函数体,对派生类数据成员初始化. 代码如下:#include<iostream.h> class base{ int x; public: base(int i) { x=i; cout<<"基类的构造函数"<<endl;

面向对象(二)——继承、派生、组合以及接口

一.继承与派生 1.1 什么是继承 继承是一种创建新类的方式,在Python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类 Python中类的继承分为:单继承和多继承 class People: # 定义父类 def __init__(self,name,age): self.name=name self.age=age def walk(self): print('%s is walking' %self) class Teacher(People): #