第四章 继承与派生 Chapter 4 Inheritance and Derivation

Slides:



Advertisements
Similar presentations
七年级数学校本课程 台山市任远中学 李锦明. 1. 最古老的过河问题 1. 最古老的过河问题 一个农民携带一只狼,一只羊和一 箱卷心菜,要借助一条小船过河。 小船上除了农民只能再带狼、羊、 卷心菜中的一样。而农民不在时, 狼会吃羊,羊会吃菜。农民如何过 河呢?
Advertisements

第五章 类的继承和派生 Inheritance/extends/derive. 教学目标 理解继承的概念和作用 派生类的定义 理解访问修饰符 protected 方法的重写 继承下的构造函数的使用 继承下的 finalize 方法的使用 理解超类和子类的关系.
12 届减数分裂复习(蔡志敬) 给你一双翅膀,让你自由翱翔!. ※真核细胞分裂的方式 有丝分裂 无丝分裂 减数分裂.
第3-2章 类与 对象 Java类的特性 教学内容: 类的私有成员与公共成员 方法的重载 构造方法 实例成员与静态成员 重点: 重载 难点:
第 9 章 物件的建構.
移动应用软件开发技术 第二讲:C++编程基础
试验的全部结果所构成的区域长度(面积或体积)
四資二甲 第三週作業 物件導向程式設計.
跳楼价 亏本大甩卖 清仓处理 买一送一 5折酬宾. 跳楼价 亏本大甩卖 清仓处理 买一送一 5折酬宾.
清仓处理 跳楼价 满200返160 5折酬宾.
第九讲 类与对象 (I)面向对象基础.
第6章 多态性与虚函数.
遺傳 龍生龍,鳳生鳳 老鼠的兒子會打洞.
11 繼承類別 11.4 建立者與破壞者 單一建立者與破壞者 11-20
類別與物件 Class & Object.
第15章 繼承與多重繼承 15-1 繼承的基礎 15-2 覆寫與隱藏父類別的成員 15-3 子類別的建構與解構子 15-4 多重繼承
第7单元 面向过程编程—— 继承与多态.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
第5章 面向对象程序设计 本章要点 5.1 面向对象程序设计概述 5.2 Java语言的面向对象程序设计 5.3 方法的使用和对象数组
内容提要 对象的生命周期 构造函数 析构函数 拷贝构造函数. 常宝宝 北京大学计算机科学与技术系
4.1 概述 4.2 类与对象的实现 4.3 对象的初始化和析构 4.4 类的包含 4.5 类模板
Derived Class 前言 衍生類別的定義 單一繼承 public, protected, 和 privated 基底類別
面向对象技术 练习2 ffh.
CHAPTER 9 建構方法 ROBERT.
第三章 C#面向对象初级编程 面向对象得程序设计越来越受到编程人员的喜爱。类和对象是面向对象程序设计中的重要概念。封装性、继承性和多态性是面向对象的特点,本章旨在全面说明C#编写面向对象程序设计的方法。
第六章 继承性和派生类 胡昊 南京大学计算机系软件所.
第六章 面向对象程序设计基础 6.1 面向对象程序设计概述 6.2 结构 6.3 类和对象 6.4 继承和派生 6.5多态性
刘胥影 东南大学计算机学院 面向对象程序设计1 2010~2011第3学期 刘胥影 东南大学计算机学院.
Object-Oriented Programming:
第3章 继承和派生.
第12讲 多继承与虚基类 多继承 虚基类.
第八章 类与对象 本章要求: 掌握类的含义与定义格式。 掌握类的无参构造函数、带参构造函数、拷贝构造函数和赋值重载函数的定义格式及作用。
C#面向对象程序设计 $7 继承和多态性.
第6章 继承和接口设计 6.1 继 承 6.2 多态性 6.3 抽象类 6.4 接口 6.5 接口在集合排序中的应用.
第二章 C++对C 在非面向对象方面的改进 更简洁,更安全.
面向对象程序设计 QQ群: Object-Oriented Programming 汽车学院.
第9讲 Java的继承与多态(一) 类的继承 子类的创建 方法覆盖.
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
第16章 虛擬與多形 16-1 虛擬函數 16-2 純虛擬函數與抽象類別 16-3 多形 16-4 虛擬繼承與虛擬解構子.
第7章 繼承/多型/介面 注意: 本投影片僅供本書上課教師使用,非經同意請勿上網轉載或供拷貝.
C++面向对象程序设计 谭浩强编著 授课教师:姬广永 QQ: 学习网站:
C++大学基础教程 第11章 多态性 北京科技大学 信息基础科学系 2019/4/8 北京科技大学.
第12讲 多继承与虚基类 多继承 虚基类.
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
第10讲 构造函数和析构函数 构造函数 析构函数 This 指针.
C++复习2----类与对象.
Inheritance -II.
第三章 数据抽象.
九年级 上册 22.3 实际问题与二次函数 (第1课时).
C++语言程序设计 C++语言程序设计 第八章 继承 C++语言程序设计.
列王紀下.
面向对象技术 练习 ffh.
THE C PROGRAMMING LANGUAGE
C++程序设计基础 主讲人:谢昕 华东交通大学信息工程学院 第十~十二讲 多态性和虚函数 2005年春季学期.
本节内容 引用类型 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
9.1.2不等式的性质 周村实验中学 许伟伟.
授课老师:龚涛 信息科学与技术学院 2016年3月 教材:《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
C++语言程序设计 C++语言程序设计 第八章 继承 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第二章 基本数据类型与表达式 第十一组 C++语言程序设计.
谭浩强编著 C++面向对象程序设计 授课教师:姬广永 学习网站:
C++语言程序设计 C++语言程序设计 第十章 多态 第十一组 C++语言程序设计.
直线系应用.
12.2提公因式法.
JAVA 程式設計與資料結構 第三章 物件的設計.
對於成員(member)存取權的限制 成員的資料被毫無限制的存取,任誰都可以指定任意值給成員,Java語言為了防止這種現象的產生,規定:有一種成員的資料不能任由類別外部的任何人隨意存取。
C++语言程序设计(第4版) 第七章 继承与派生 数学与统计科学学院 胡凤珠.
下列哪些是不等式 的解? 10, 9 , , –1,  全部皆是 你認為不等式 有多少個解? 5 個 無限多個
資料結構與C++程式設計進階 C++與資料結構 講師:林業峻 CSIE, NTU 7/ 5, 2010.
2.2.2双曲线的简单几何性质 海口市灵山中学 吴潇.
Presentation transcript:

第四章 继承与派生 Chapter 4 Inheritance and Derivation 14:41:26

继承 (Inheritance) 当已经实现了一个类后,我们可以从这个类 派生出新的类,这种派生机制就叫做继承。 比如已经有了学生类,那么 研究生也是学生类的一种, 因此学生所有的属性他都具备 但又有自己独特的属性 比如:研究方向,导师 继承使得代码的可重用性大大增加 Student GradStudent 14:41:26

练习 定义1个CPerson类,其属性(保护类型)有: 姓名、性别和年龄 从CPerson类派生出CStudent类,增加属性: 学号、入学时间和入学成绩; 从CPerson类派生出CTeacher类,增加属性: 职务、部门和工作时间; CStudent类派生出CGraduate类,增加属性: 研究方向和导师; CGraduate和CTeacher共同派生出在职研究生 类CGradOnWork 14:41:26

继承体系 CPerson CTeacher CStudent CGraduate CGradOnWork 本章主要围绕这个例子展开!!! 14:41:26

继承方式 继承语法形式 class B {……}; class D : [private | protected | public] B { 可分为 私有继承:private 保护继承:protected 公有继承:public 14:41:26

派生类和基类的关系 派生类是在基类的继承上添加成员,因此: 派生类继承了基类的所有成员,尽管有些 继承的成员可能是不可访问的 this指针 基类成员 新增加成员 this指针 派生类成员 14:41:26

关于this指针 class Point{ private: double x,y; public: Point(double x, double y){ this->x = x; this->y = y; } static void f(){this->x=0;}//错 }; int main(){ Point p(1,2); this指针是隐含在成员函数的参数列表里,由C++编译器为你自动加上,存放的是对象所占的内存首地址。 由于static成员是类层次上的成员,所以不能在静态成员函数中使用this指针 14:41:26

基类与派生类的关系 派生类对基类成员的访问形式 通过派生类对象直接访问基类成员 在派生类成员函数中直接访问基类成员 通过基类名字限定访问被重载的基类成员 名 下面举例说明 14:41:26

基类与派生类的关系 class B{ //B为基类 int x; public: void set(int i){ x=i;} void setx(int i){ x=i;} void printx(){ cout<<"x="<<x<<endl; } }; 14:41:26

class D:public B{ //D为B的派生类 int y; public: void sety(int p){y=p;} void printy(){ cout<<"y="<<y<<endl; } void setxy(int i,int j){ setx(i); //在派生类成员函数中直接访问基类成员 y=j; } void set(int i,int j){ B::set(i); //访问基类set成员 };

//通过对象访问…. int main(){ D obj; obj. setx(2); //访问基类成员 obj //通过对象访问…... int main(){ D obj; obj.setx(2); //访问基类成员 obj.printx(); //访问基类成员, 输出 x=2 obj.sety(3); //访问派生类成员 obj.set(1,2); //访问派生类set成员 obj.B::set(3); //访问基类set成员 }

基类与派生类对象的关系 14:41:26

基类与派生类对象的关系 基类对象与派生类对象之间可以这样赋值: 把派生类对象赋值给基类对象。 base = derive; //对象 把派生类对象的地址赋值给基类指针: Base *pBase=&derive; //指针 用派生类对象初始化基类对象的引用。 Base base = &derive; //指针 反之则不行,即基类类型的对象或者指针不 能赋值给派生类对象或指针 14:41:26

把派生类对象赋值给基类对象的例子。 class A { int a; public: void setA(int x){ a=x; } int getA(){ return a;} }; class B:public A{ int b; void setB(int x){ b=x; } int getB(){ return b;} int main() { A a,*pa; B b,*pb; a = b; pa = &b; b = a;//错误 pb = &a;//错误 }

基类与派生类对象的关系 说明: ① 不论以哪种方式把派生类对象赋值给基类 对象,都只能访问到派生类对象中的基类子 对象部份的成员,不能访问派生类的自定义 成员。 ②只能把派生类对象赋值给基类对象,不能 把基类对象赋值给派生类对象。 切割现象 14:41:26

公有继承 public 最常见的派生方式 维持基类成员的可访问性 派生类不可直接访问基类的private 成员,可通过基类的公有成员函数 访问 14:41:26

公有接口 私有数据 x Base Derived x y class Base{ int x; //私有 public: void Setx(int x){this->x=x;} void Showx() {cout<<x<<endl;} }; 公有接口 私有数据 Setx() Showx() x Base class Derived:public Base{ int y; public: void Set(int x, int y) { Setx(x); this->y=y; } void Show() { Showx(); cout<<y<<endl; }; Derived Setx() Showx() x Set() Show() y 14:41:26

公有继承 int main() { Derived d; //派生类对象 d.setx(1); //基类里的成员函数 d.showx(); //同上 d.set(2,3); //派生类自己的函数 d.show(); //同上 } 14:41:26

私有继承 基类的中的public成员在派生类中变成了 private 基类的private成员在派生类中仍然不可访问。 class Base{ int x; public: void setx(int x){this->x=x;} void showx(){cout<<x<<endl;} }; 14:41:26

公有接口 私有 x Base Derived x y class Derived:private Base{ int y; //私有 public: void Set(int x, int y) { Setx(x); this->y=y; } void Show() { Showx(); cout<<y<<endl; }; 公有接口 私有 Setx() Showx() x Base Derived Setx() Getx() Showx() x int main(){ Derived d; d.setx(1);//错误 d.showx();//错误 d.set(2,3);d.show(); } Set() Show() y

保护成员 基类中protected的成员 类内部:可以访问 类的使用者:不能访问 类的派生类成员:可以访问类似于 public protected成员的权限介于private和public之 间,主要体现在继承过程中 类似于私有成员 14:41:26

class B { private: int i; protected: int j; public: int k; }; class D: public B {public: void f() { i=1;//cannot access j=2; k=3; } int main() { B b; b.i =1;//cannot access b.j=2; //cannot access b.k=3; } 接口 私有数据 k j i B类 D类 k j i f()

class Base{ int x; protected: int getx(){ return x; } public: void setx(int n){ x=n; } void showx(){ cout<<x<<endl; } }; class Derived:protected Base{ int y; void sety(int n){ y=n; } void sety(){ y=getx();} //访问基类的保护成员 void showy(){ cout<<y<<endl; } 保护继承 基类的public成员在派生类中会变成protected成员,基类的protected和private成员在派生类中保持原来的访问权限。 int main(){ Derived obj; obj.setx(10); //错误 obj.sety(20); obj.showx(); //错误 obj.showy(); }

总结:基类成员在派生类中的访问权限 基类的私有成员只有在本类成员函数中访问,在派生类中不能访问!!! 基类 派生类 public继承 protected继承 private继承 public protected private √ 基类的私有成员只有在本类成员函数中访问,在派生类中不能访问!!!

继承机制下的构造函数和析构函数 之间已经介绍过:类中包含对象成员时的构 造顺序为: 先构造成员 再构造自身 14:41:26

class A { public: A() { cout<<"Constructing A"<<endl;} ~A(){ cout<<"Destructing A"<<endl;} }; class B { B() { cout<<"Constructing B"<<endl;} ~B(){ cout<<"Destructing B"<<endl;} 14:41:26

如果没有继承,对象成员先构造,再构造自身对象 Constructing B Constructing A Constructing C Destructing C Destructing A Destructing B class C { public: C() { cout<<"Constructing C"<<endl;} ~C(){ cout<<"Destructing C"<<endl;} B b; A a; }; int main() { C c; } 如果没有继承,对象成员先构造,再构造自身对象 如果存在继承: class B:public A{} class C:public B{} 结果又当如何?

派生类构造函数的定义 之前介绍过对象成员的构造需要初始化列表 基类对象事实上是派生类对象的一部分,创建派 生类对象必须要调用基类的构造函数,并向它们 传递参数,以完成基类子对象的建立和初始化。 派生类只能采用构造函数初始化列表的方式向基 类或成员对象的构造函数传递参数,形式如下: 派生类构造函数名(参数表): 基类构造函数名 (参数表),成员对象名1(参数表),…{ //…… } 14:41:26

派生类构造函数的定义 class Base{ private: int x; public: Base(int a){ x=a; cout<<"Base constructor x="<<x<<endl; } ~Base(){ cout<<"Base destructor..."<<endl; } }; 14:41:26

class Derived: public Base{ private: int y; public: Derived(int a,int b): Base(a){ //派生类构造函数的 初始化列表 y=b; cout<<"Derived constructor y="<<y<<endl; } ~Derived(){ cout<<"Derived destructor..."<<endl; } }; int main(){ Derived d(1,2);

构造函数和析构函数调用次序 派生类对象的构造 先构造基类 再构造成员 最后构造自身 基类构造顺序由派生层次决定:最远的基类 最先构造 成员构造顺序和定义顺序相同 析构函数的析构顺序与构造顺序相反 14:41:26

class A { public: A() { cout<<"Constructing A"<<endl;} ~A(){ cout<<"Destructing A"<<endl;} }; class B { B() { cout<<"Constructing B"<<endl;} ~B(){ cout<<"Destructing B"<<endl;} class C { C() { cout<<"Constructing C"<<endl;} ~C(){ cout<<"Destructing C"<<endl;}

class D:public C { public: D() { cout<<"Constructing D"<<endl;} ~D(){ cout<<"Destructing D"<<endl;} B b; A a; C c; }; int main() D d; } Constructing C Constructing B Constructing A Constructing D Destructing D Destructing C Destructing A Destructing B

构造函数和析构函数的构造规则 派生类的构造函数只负责直接基类的初始化 C++语言标准有一条规则:如果派生类的基类 同时也是另外一个类的派生类,则每个派生 类只负责它的直接基类的构造函数调用。 这条规则表明当派生类的直接基类只有带参 数的构造函数,但没有默认构造函数时(包 括缺省参数和无参构造函数),它必须在构 造函数的初始化列表中调用其直接基类的构 造函数,并向基类的构造函数传递参数,以 实现派生类对象中的基类子对象的初始化。 14:41:26

当同时存在直接基类和间接基类时,每个派生类只负 责其直接基类的构造。 class A { //基类 int x; public: A(int aa) { x=aa; cout<<"Constructing A"<<endl; } ~A(){ cout<<"Destructing A"<<endl; } };

class B:public A {//A是B的直接基类 public: B(int x):A(x){ cout<<"Constructing B"<<endl; } }; class C :public B{//B是C直接基类,而A是间接基类 C(int y):B(y){ cout<<"Constructing C"<<endl; } void main(){ C c(1); }

多重继承 C++允许一个类从一个或多个基类派生。如果一个 类只有一个基类,就称为单一继承。如果一个类 具有两个或两个以上的基类,就称为多重继承。 多继承的形式如下: class 派生类名:[继承方式] 基类名1,[继承方式] 基类 名2, … { …… }; 其中,继承方式可以是public、protected、 private 14:41:26

多继承的概念和应用 14:41:26

多继承的概念和应用 class Base1{ private: int x; protected: int getx(){ return x; } public: void setx(int a=1){ x=a; } }; 14:41:26

class Base2{ private: int y; public: void sety(int a){ y=a; } int gety(){ return y; } }; class Base3{ int z; void setz(int a){ z=a; } int getz(){ return z; }

class Derived:public Base1,public Base2,public Base3{ private: int d; public: void setd(int a){ d=a; } void display(); }; void Derived::display(){ cout<<"Base1....x="<<getx()<<endl; cout<<"Base2....y="<<gety()<<endl; cout<<"Base3....z="<<getz()<<endl; cout<<"Derived..d="<<d<<endl; } void main(){ Derived obj; obj.setx(1); obj.sety(2); obj.setz(3); obj.setd(4); obj.display();

多继承下的二义性 在多继承方式下,派生类继承了多个基类的 成员,当两个不同基类拥有同名成员时,容 易产生名字冲突问题。 类A和类B是MI的基类,它们都有一个成员函 数f,在类MI中就有通过继承而来的两个同名 成员函数f。 14:41:26

class A { public: void f(){ cout<<"From A"<<endl;} }; class B { void f() { cout<<"From B"<<endl;} class MI: public A, public B { void g(){ cout<<"From MI"<<endl; } void main(){ MI mi; mi.f(); //错误 mi.A::f(); //正确 }

多继承的构造函数与析构函数 派生类必须负责为每个基类的构造函数提供 初始化参数,构造的方法和原则与单继承相 同。 构造函数的调用次序仍然是先基类,再对象 成员,然后才是派生类的构造函数。 基类构造函数的调用次序与它们在被继承时 的声明次序相同,与它们在派生类构造函数 的初始化列表中的次序没有关系。 多继承方式下的析构函数调用次序仍然与构 造函数的调用次序相反。 14:41:26

虚拟继承 引入的原因:重复基类 DeptHead Faculty Person name … Administrator name … 派生类间接继承同一基类使得间接基类(Person)在派生类中有多份拷贝,引发二义性。 14:41:26

虚拟基类在派生类中只存在一份拷贝,解决了基类数据成员的二义性问题 14:41:26

虚拟继承的实现 虚拟继承virtual inheritance的定义 语法 虚基类virtual base class class derived_class : virtual […] base_class 虚基类virtual base class 被虚拟继承的基类 在其所有的派生类中,仅出现一次 14:41:26

类A是类B、C的基类,类D从类B、C继承,在类D中调用基类A的成员会产生二义性。 class A { public: void vf() { cout<<"I come from class A"<<endl; } }; class B: public A{}; class C: public A{}; class D: public B, public C{}; int main() { D d; d.vf(); // 错误 d.B::vf(); // 正确 d.C::vf(); //正确 } A Vf() A Vf() B Vf() C Vf() D B.A::Vf() C.A::Vf()

改为虚拟继承不会产生二义性。 class A { public: void vf() { cout<<"I come from class A"<<endl; } }; class B: virtual public A{}; class C: virtual public A{}; class D: public B, public C{}; void main() { D d; d.vf(); // 正确 } 菱形继承 A Vf() B Vf() C Vf() D A::Vf()

虚拟继承的构造次序 虚基类初始化时构造函数的调用顺序不同; 若基类由虚基类派生而来,则派生类必须提供对 间接基类的构造(即在构造函数初始列表中构造 虚基类,无论此虚基类是直接还是间接基类) 调用顺序的规定: 先调用虚基类的构造函数,再调用非虚基类 的构造函数 若同一层次中包含多个虚基类,这些虚基类的 构造函数按它们说明的次序调用 若虚基类由非基类派生而来,则仍然先调用基 类构造函数,再调用派生类构造函数 14:41:26

虚拟继承的实现 虚基类的执行次序分析。 class A { int a; public: A(){ cout<<"Constructing A"<<endl; } }; class B { B(){ cout<<"Constructing B"<<endl;} 14:41:26

//B是普通基类,A是虚基类 class B1:public B ,virtual public A{ public: B1(int i){ cout<<"Constructing B1"<<endl; } }; //A是普通基类,B是虚基类 class B2:public A,virtual public B { B2(int j){ cout<<"Constructing B2"<<endl; } class D: public B1, public B2 { D(int m,int n): B1(m),B2(n){ cout<<"Constructing D"<<endl; A a; int main(){ D d(1,2); 3 1 4 5 2 6 7 8

虚拟继承的实现 虚基类由最终派生类初始化 在没有虚拟继承的情况下,每个派生类的 构造函数只负责其直接基类的初始化。但 在虚拟继承方式下,虚基类则由最终派生 类的构造函数负责初始化。 在虚拟继承方式下,若最终派生类的构造 函数没有明确调用虚基类的构造函数,编 译器就会尝试调用虚基类不需要参数的构 造函数(包括缺省、无参和缺省参数的构 造函数),如果没找到就会产生编译错误。 14:41:26

类A是类B、C的虚基类,类ABC从B、C派生,是 继承结构中的最终派生类,它负责虚基类A的 初始化。 class A { int a; public: A(int x) { a=x; cout<<"Virtual Bass A..."<<endl; } };

class B: virtual public A { public: B(int i):A(i){ cout<<"Virtual Bass B..."<<endl; } }; class C: virtual public A{ int x; C(int i):A(i){ cout<<"Constructing C..."<<endl; x=i; } class ABC:public C, public B { ABC(int i,int j,int k):C(i),B(j),A(i) //L1,这里必须对A进行初始化 { cout<<"Constructing ABC..."<<endl; } void main(){ ABC obj(1,2,3);