内容提要 代码重用 类的继承 多态 抽象类 多重继承 虚拟继承. 常宝宝 北京大学计算机科学与技术系

Slides:



Advertisements
Similar presentations
第6章 继承.
Advertisements

第8章 继承和派生 8.1 继承与派生 8.2 派生类对基类成员的访问 8.3 派生类的构造函数与析构函数 8.4 多重继承与虚基类.
课程 要求 参考 书目 课程 内容 课程 练习.
面向对象的C++程序设计基础 第 4 章 继承与派生.
第10章 虚函数与多态性.
7.2 访问控制 —— 公有继承 公有继承练习 //Point.h #ifndef _POINT_H #define _POINT_H class Point { //基类Point类的定义 public: //公有函数成员 void initPoint(float x = 0, float.
2-7、函数的微分 教学要求 教学要点.
计算机可视化编程 基于Visual C++6.0的面向对象编程 第 四 讲 主讲教师:隋振                学时:32.
C++面向对象程序设计 第八章 继承和派生.
预备知识——C++、类与对象 C++要素 类和对象 模版 类的继承.
第12章 组合与继承 欢迎辞 第14次见面!.
面向对象程序设计 第三章 C++面向对象程序设计 武汉大学 赵小红.
第6章 继 承 主讲人:陈南京.
第四章 继承和派生类 汽车 专用汽车 运输汽车 货车 客车 消防车 洒水车 最普遍、最一般,可以自行驱动 含有汽车的特性,同时与汽车有不同
第14章 c++中的代码重用.
C++语言程序设计 第七章 继承与派生 清华大学 郑 莉.
C++ 面对对象程序设计 授课老师:.
第11章 类的继承和派生 继承是面向对象程序设计方法的四个基本特征之一,是程序代码可重用性的具体体现。
第6章 多态性与虚函数.
第10讲 Java面向对象编程基础(4) 教学目标 主要内容.
类和对象 潘荣江 山东大学计算机科学与技术学院
版权所有 复制必究 第 3 章 C++面向对象程序设计.
授课老师:龚涛 信息科学与技术学院 2018年4月 教材:《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
第10章 C++面向对象程序设计 本章导读 C语言是一种结构化程序设计语言,它是面向过程的,在处理较小规模的程序时一般比较容易实现,而当程序规模较大时,C语言就显示出了它的不足。在这种情况下C++应运而生,C++语言是从C语言演变而来的,它保留了C语言的所有优点,同时也增加了面向对象的功能。现在C++已成为程序设计中应用最广泛的一种语言。
第11讲 类的继承 1. 类的继承的概念 2. 类的单继承机制 3. 单继承中的构造函数和析构函数.
第12讲 多继承与虚基类 多继承 虚基类.
辅导课程六.
第9章 类和对象(一) 9.1 面向对象的基本概念 9.2 类与对象的声明和定义 9.3 成员函数 9.4 对象的访问 9.5 对象的存储.
第1章 C++基础.
刘胥影 东南大学计算机学院 面向对象程序设计1 2010~2011第3学期 刘胥影 东南大学计算机学院.
第八章 继承与派生 丘志杰 电子科技大学 计算机学院 软件学院.
第十一章 继承和派生. 主讲教师:全红艳 第十一章 继承和派生.
用event class 从input的root文件中,由DmpDataBuffer::ReadObject读取数据的问题
第七章 操作符重载 胡昊 南京大学计算机系软件所.
C++大学基础教程 第9章 继承与派生 北京科技大学.
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
简单介绍 用C++实现简单的模板数据结构 ArrayList(数组, 类似std::vector)
第11讲 类的继承 1. 类的继承的概念 2. 类的单继承机制 3. 单继承中的构造函数和析构函数.
$9 泛型基础.
内容 构造函数 析构函数 对象数组 对象指针 this指针 const数据保护. 类和对象的使用 潘荣江 山东大学.
C++复习3 ----类的继承与派生.
第14讲 运算符重载 运算符重载的概念、方法和规则 运算符重载作为类的成员函数 运算符重载作为类的友元函数 特殊运算符的重载 类型转换函数.
C#面向对象程序设计 $6 深入理解类.
C++语言程序设计 第四章 类与对象.
Classes (1) Lecture 6.
第13讲 多态 友员函数 多态性与虚函数 纯虚函数和抽象类.
补课.
C++程序设计— 多态与虚函数 主讲:资讯系张玉宏.
授课老师:龚涛 信息科学与技术学院 2016年3月 教材:《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
本节内容 类成员的访问控制 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
第九节 赋值运算符和赋值表达式.
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第八章 继承 C++语言程序设计.
C++语言程序设计教程 第8章 继承与派生 第8章 继承与派生 制作人:杨进才.
辅导课程十五.
第7章 模板 陈哲 副教授 南京航空航天大学 计算机科学与技术学院.
本节内容 C语言的汇编表示 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
C++语言程序设计 C++语言程序设计 第十一章 异常处理 C++语言程序设计.
C++与数据结构简明教程 第五章 类和对象.
C++语言程序设计 C++语言程序设计 第八章 继承 C++语言程序设计.
本节内容 动态链接库 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
本课程学习目标 培养编程技能 开启智慧之门.
C++语言程序设计 C++语言程序设计 第十章 多态 第十一组 C++语言程序设计.
第十二讲 继承 与 派生.
创建、启动和关闭Activity 本讲大纲: 1、创建Activity 2、配置Activity 3、启动和关闭Activity
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
本节内容 this指针 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
Presentation transcript:

常宝宝 北京大学计算机科学与技术系 chbb@pku.edu.cn

内容提要 代码重用 类的继承 多态 抽象类 多重继承 虚拟继承

代码重用 面向对象程序设计追求的目标之一是代码重用(code reuse)。 类代码重用的方法之一是组合。即在类中定义对象成员,而对象成员是已存在类的对象。 例如已经定义一个描述“引擎”的类,在定义“汽车”类时,可以为汽车类定义一个“引擎”类的对象成员。 对象和其对象成员之间的关系是一种“has-a”关系,如:一辆汽车有一个引擎。

代码重用 在面向对象的程序设计方法中,代码重用的另一种方式是继承。 如果新设计的类和已存在的类具有相似的功能和相似的接口,可以通过继承的方式重用已存在的类。例如: 定义了“形状”类,则“圆”、 “正方形”和“三角形”可以 继承“形状”类的特性。 在继承关系中,被继承的 类称为基类,通过继承关 系新建的类称为派生类。 派生类和基类之间的关系 是“is-a”关系。例如:“圆” 是一种“形状”。

继承 继承意味着派生类继承了基类的所有特性,基类的所有数据成员和成员函数自动成为派生类的数据成员和成员函数。如: “形状”具有位置、大小、颜色等属性,可以移动、绘制等,由其派生出来的“圆”同样有这些属性和操作。 派生类不完全等同于基类,派生类可以添加自己特有的特性,即可以为派生类增加新的数据成员和成员函数。如: “圆”有半径,“正方形”有边长或顶点位置等。 派生类可以重定义基类中不满足派生类要求的特性,既可以重新定义基类中的成员函数。如: 计算“圆”的面积不能直接使用“形状”面积的计算方法。 在继承关系中,基类的接口是派生类接口的子集,派生类支持集类所有的公有成员函数。

继承 派生类的定义方法如下: class 派生类的类名: 继承类型 基类的类名 { … }; 继承类型有三种: public 公有继承 protected 保护继承 private 私有继承 例子:student.h student.cpp student_test.cpp 从类student派生出类graduate_student

继承 派生类继承了基类的所有数据成员和成员函数,包括静态数据成员和静态成员函数。如: graduate_student gs(“罗小四”, “0987654321”, “杨振宁”); gs.set_score(98.5); 派生类可以增加新的数据成员和成员函数,此时要在派生类中定义这些数据成员和成员函数。如: graduate类中新增了数据成员advisor、title 新增了成员函数get_advisor()、set_thesis_title(…)、get_thesis_title() 派生类可以重新定义基类中不能满足要求的成员函数。如: graduate类中重新定义了基类中的成员函数display()

成员函数的重定义 重新定义一个成员函数时,可以调用基类中的旧版成员函数,此时应使用作用域指示符。如: void graduate_student::display() const { student::display(); cout << "Advisor:" << advisor << endl; cout << "Thesis:" << title << endl; } 若在派生类中重新定义了某个成员函数,通过派生类对象调用该成员函数,调用的是新版的成员函数,通过基类对象调用的是基类中的旧版成员函数。如: student s("王二", "1234567890"); graduate_student gs(“罗小四”, “0987654321”, “杨振宁”); s.display(); gs.display();

名字隐藏 如果在派生类中重新定义了基类中的成员函数,基类中该成员函数及其所有重载版本在派生类中均不再可用。 参见namehiding.cpp class Base { public: int f(); int f(float); ... }; class Derived2 : public Base { int main() { float s = 10.68; Derived2 d2; x = d2.f(); d2.f(s);//错误,由于派生类重 //新定义了f(),导致基类中的 //f(float)不再可用。 } 参见namehiding.cpp

基类中成员在派生类中的可见性 派生类中可以存取或调用基类中的保护成员和公有成员,但不能存取或调用基类的私有成员。(保护成员和私有成员的区别) class base { public: void set(int a, int b, int c); private: int m1; protected: int m2; int m3; }; class derived: public base { public: void f() { cout<<“m1=”<<m1<<endl;//错误 cout<<"m2="<<m2<<endl; cout<<"m3="<<m3<<endl; } }; //m1 是基类的私有成员,派生类的成员//函数无权存取。

继承类型 基类中成员在派生类中的存取权限由继承类型确定,继承类型可以改变基类中成员在派生类中的存取权限。 在定义派生类时,如果没有指出继承类型,则默认为私有继承。 参见例子:access.cpp 通过私有继承和保护继承得到的派生类不支持基类的接口,因而派生类和基类之间不存在“is-a”关系。 在C++程序设计中,私有继承和保护继承很少使用。

派生类对象的内存布局 在为派生类对象分配存储空间时,其中包含一个基类的对象。如:

派生类对象的构造和析构 在派生类构造函数调用前,会自动调用基类构造函数。 在派生类析构函数调用后,会自动调用基类析构函数。 如果需要调用带参数的基类构造函数,应在定义派生类构造函数时,以初始化列表的方式给出,形式如下: 派生类的类名::派生类的类名(构造函数参数表):初始化表 {...} graduate_student::graduate_student(char *pname, char *pid, char*pan):student(pname, pid) { strcpy(advisor, pan); strcpy(title, "" ); } ... graduate_student gs("罗小四", "0987654321", "杨振宁");

派生类对象的构造和析构 对于默认拷贝构造函数而言,在调用派生类的默认构造函数之前会自动调用基类的默认拷贝构造函数。 一旦为派生类定义了拷贝构造函数,则不会自动调用基类的(默认)拷贝构造函数,而是自动调用基类的默认构造函数,此时应在派生类拷贝构造函数后的初始化列表中调用基类的(默认)拷贝构造函数。如: graduate_student::graduate_student(graduate_student& gs):student(gs) { strcpy(advisor, gs.advisor ); strcpy(title, gs.title ); }

赋值运算符重载函数 下列特殊的基类成员函数不会被派生类继承: 构造函数 拷贝构造函数 析构函数 赋值运算符重载函数operator=(...) 如果没有为派生类定义赋值运算符重载函数,编译程序会生成默认赋值运算符重载函数,并且会自动调用基类的赋值运算符重载函数。 如果程序员为派生类重新定义了赋值运算符重载函数,程序员需要显式调用基类的赋值运算符重载函数,此时不会自动调用基类赋值运算符重载函数。调用方式如下: 基类名::operator=(...); 参见例子assignment.cpp

基向类型转换(upcasting) 由于派生类是一种特殊的基类(is-a关系),派生类对象可以作为基类对象使用。 指向派生类对象的指针可以赋值给指向基类对象的指针。派生类对象可以作为基类对象引用的初值。如果是函数调用,派生类对象的实参可以传递给基类对象引用的形参,派生类对象指针实参可以传递给基类对象指针的形参,这称为基向类型转换。 void func(student& s) {...} void func(student* s) {...} void main() { graduate_student gs(“罗小四”, “0987654321”, “杨振宁”); student& rs = gs; //正确 student* ps; ps = &gs; //正确 func(gs); //正确 func(&gs); //正确 }

基向类型转换 当使用了基向类型转换后,用基类引用(或指向基类对象指针)存取派生类对象的数据成员或成员函数时,只能存取从基类中继承来的数据成员或成员函数。如: graduate_student gs(“罗小四”, “0987654321”, “杨振宁”); student& rs = gs; cout<<rs.get_advisor()<<endl;//错误 如果派生类中重新定义了基类中的成员函数,用基类引用(或指向基类对象指针)调用重定义了的成员函数时,调用的仍然是基类中的旧版成员函数。如: graduate_student gs(“罗小四”, “0987654321”, “杨振宁”); student& rs = gs; rs.display();//调用了student::display()

多态 理想的情况是成员函数调用应和引用的具体对象或指针实际指向的对象有关,当基类引用实际引用的是派生类的对象或基类指针实际指向的是派生类对象时,调用派生类中重定义的新版成员函数,而当基类引用实际引用的是基类的对象或基类指针实际指向的是基类对象时,调用基类中的旧版成员函数。这种根据具体对象决定调用不同版本成员函数的特性称为多态性。 为实现多态需要使用虚函数,把一个成员函数定义为虚函数的办法是在返回类型前使用保留字virtual。如: class student { ... virtual void display() const; };

虚函数 虚函数具有传递性,当基类中某个成员函数被声明为虚函数,派生类中重定义该成员函数时,即使没有使用保留字virtual,派生类中的该成员函数也是虚函数。如: class graduate_student:public student { ... void display() const;//虚函数 }; 如果派生类在重定义基类的虚函数时,改变了函数的参数个数、参数类型或返回类型等,则重新定义的函数在派生类中不再是虚函数。 class graduate_student:public student { ... void display( int );//不是虚函数 };

多态 利用基类指针或基类引用调用虚函数时会呈现多态性,具体调用的函数和此时引用或实际指向的对象有关,若实际指向派生类对象,调用派生类中的新版函数,若实际指向基类对象,则调用基类中的旧版函数。如: graduate_student gs(“罗小四”, “0987654321”, “杨振宁”); student& rs = gs; rs.display();//调用了graduate_student::display() 例子: student_virtual.h student_virtual.cpp student_virtual_test.cpp

虚函数 普通函数不能声明为虚函数。 静态成员函数不能声明为虚函数。 内联成员函数不能声明为虚函数。 构造函数不能声明为虚函数。 析构函数可以声明为虚函数。 多态性是面向对象程序设计语言的重要特性,不支持多态的程序设计语言不能称为面向对象的程序设计语言。

纯虚函数 通过基类指针或引用调用的成员函数在必须在基类中定义,否则就不能通过基类指针或引用调用。 有时候,某些成员函数只能在派生类中定义,但又希望支持多态,即希望能通过基类指针或引用调用该成员函数,此时可以在基类中定义纯虚函数。定义纯虚函数的办法为: class 类名 { ... virtual 返回类型 成员函数名(参数表) = 0; }; 所谓纯虚函数指的是暂时不用实现的成员函数。其具体实现可以延迟到派生类中进行。纯虚函数一定是虚函数。 包含纯虚函数的类称为抽象类。

纯虚函数 例子: abstract.cpp 抽象类仅仅定义了类及其派生类的接口。只能用来继承。 不能创建抽象类的对象。因为抽象类中含有没有实现的函数,即纯虚函数。 Instrument a;//错误

纯虚函数 派生类必须重新定义纯虚函数,如果派生类中还有没有重新定义的纯虚函数,则派生类还是抽象类。 虽然不能创建抽象类的对象,但可以创建指向抽象类对象的指针和抽象类对象的引用。如: Wind flute; Percussion drum; Instrument *pi;//正确 pi = &drum; Instrument& ri = flute; //正确

多重继承 可以同时从多个基类派生新类,这称为多重继承。 在多重继承中,多个基类之间用逗号间隔,形式如下: class 派生类: 继承类型 基类1, 继承类型 基类2, ...,继承类型 基类n { … }; 例子: multiple_inheritance.h multiple_inheritance.cpp 在多重继承中,派生类继承了所有基类的数据成员和成员函数.如: sleeping_sofa ss; ss.watch_TV(); //来自基类 sofa ss.fold_out(); ss.sleep(); //来自基类 bed

多重继承中的名字冲突 在多重继承中,如果多个基类中含有同名成员,此时会出现名字冲突。如: sleeping_sofa ss; ss.set_weight(5);//错误,发生了名字冲突 在发生冲突时,应通过指明基类的方式加以区分。如: ss.sofa::set_weight(5);//正确 ss.bed::set_weight(5);//正确

多重继承

多重继承 在多重继承中,一个基类可以通过不同的路径被一个派生类多次继承,从而在派生类对象中产生基类对象的多个拷贝。 为避免这种情况出现,引入虚拟继承的概念。为使派生类中只有一个基类对象拷贝,从该基类继承采用虚拟继承。此时,基类称为虚基类,形式如下: class b1: virtual public base { ... }; class b2: virtual public base { ... };

多重继承 多重继承非常复杂,它的引入导致了许多问题需要解决,多重继承是C++语言最受批评的特性之一。 程序员一般很少使用多重继承。 所以对于多重继承,了解即可。

上机练习内容 《C++程序设计教程》p.380 练习16.1 、16.2、16.3