补课.

Slides:



Advertisements
Similar presentations
面向对象程序设计 QQ群: Object-Oriented Programming 汽车学院.
Advertisements

课程 要求 参考 书目 课程 内容 课程 练习.
第10章 虚函数与多态性.
7.2 访问控制 —— 公有继承 公有继承练习 //Point.h #ifndef _POINT_H #define _POINT_H class Point { //基类Point类的定义 public: //公有函数成员 void initPoint(float x = 0, float.
内容提要 代码重用 类的继承 多态 抽象类 多重继承 虚拟继承. 常宝宝 北京大学计算机科学与技术系
面向对象程序设计 第三章 C++面向对象程序设计 武汉大学 赵小红.
第14章 c++中的代码重用.
C++ 面对对象程序设计 授课老师:.
第11章 类的继承和派生 继承是面向对象程序设计方法的四个基本特征之一,是程序代码可重用性的具体体现。
C++中的声音处理 在传统Turbo C环境中,如果想用C语言控制电脑发声,可以用Sound函数。在VC6.6环境中如果想控制电脑发声则采用Beep函数。原型为: Beep(频率,持续时间) , 单位毫秒 暂停程序执行使用Sleep函数 Sleep(持续时间), 单位毫秒 引用这两个函数时,必须包含头文件
第6章 多态性与虚函数.
在PHP和MYSQL中实现完美的中文显示
Using C++ The Weird Way Something about c++11 & OOP tricks
Object-Oriented Programming in C++ 第四章 运算符重载
Derived Class 前言 衍生類別的定義 單一繼承 public, protected, 和 privated 基底類別
第11章 运算符重载 什么是运算符重载 运算符重载的方法 几个特殊的运算符的重载 自定义类型转换运算符 运算符重载实例.
版权所有 复制必究 第 3 章 C++面向对象程序设计.
第3章 继承和派生.
西安交通大学 计算机教学实验中心 大学C++程序设计教程 西安交通大学 计算机教学实验中心
Chapter 14 Templates.
授课老师:龚涛 信息科学与技术学院 2018年3月 教材: 《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
第11讲 类的继承 1. 类的继承的概念 2. 类的单继承机制 3. 单继承中的构造函数和析构函数.
第12讲 多继承与虚基类 多继承 虚基类.
辅导课程六.
第一单元 初识C程序与C程序开发平台搭建 ---观其大略
第九章 多态性 丘志杰 电子科技大学 计算机学院 软件学院.
第十一讲 运算符重载 与类型转换.
用event class 从input的root文件中,由DmpDataBuffer::ReadObject读取数据的问题
第七章 操作符重载 胡昊 南京大学计算机系软件所.
10 多載函數 10.1 多載概論 多載一般函數 多載成員函數 10-3
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
C++大学基础教程 第11章 多态性 北京科技大学 信息基础科学系 2019/4/8 北京科技大学.
简单介绍 用C++实现简单的模板数据结构 ArrayList(数组, 类似std::vector)
第11讲 类的继承 1. 类的继承的概念 2. 类的单继承机制 3. 单继承中的构造函数和析构函数.
$9 泛型基础.
潘爱民 C++ Overview 潘爱民
C++复习3 ----类的继承与派生.
第14讲 运算符重载 运算符重载的概念、方法和规则 运算符重载作为类的成员函数 运算符重载作为类的友元函数 特殊运算符的重载 类型转换函数.
C#面向对象程序设计 $6 深入理解类.
第13讲 多态 友员函数 多态性与虚函数 纯虚函数和抽象类.
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
第4章 Excel电子表格制作软件 4.4 函数(一).
本节内容 类成员的访问控制 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
C++语言程序设计 C++语言程序设计 第八章 继承 C++语言程序设计.
C++大学基础教程 第10章 运算符重载 北京科技大学 2019/5/7 北京科技大学.
C++语言程序设计 C++语言程序设计 第十章 多态 第十一组 C++语言程序设计.
第九节 赋值运算符和赋值表达式.
C++程序设计基础 主讲人:谢昕 华东交通大学信息工程学院 第十~十二讲 多态性和虚函数 2005年春季学期.
多层循环 Private Sub Command1_Click() Dim i As Integer, j As Integer
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第八章 继承 C++语言程序设计.
辅导课程十五.
授课老师:龚涛 信息科学与技术学院 2016年3月 教材:《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
#include <iostream.h>
第7章 模板 陈哲 副教授 南京航空航天大学 计算机科学与技术学院.
C++语言程序设计 C++语言程序设计 第八章 继承 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第十一章 异常处理 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第十章 多态 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第八章 继承 C++语言程序设计.
《数据结构与算法设计》第一部分 面向对象的C++程序设计基础.
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
本课程学习目标 培养编程技能 开启智慧之门.
C++语言程序设计 C++语言程序设计 第十章 多态 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第十一章 异常处理 C++语言程序设计.
C++程序语言设计 Chapter 14: Templates.
使用Fragment 本讲大纲: 1、创建Fragment 2、在Activity中添加Fragment
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
Presentation transcript:

补课

静态联编 (使用对象调用) 编译时决定 通过函数重载和模板体现 多态 动态联编 (使用指针或引用调用) 运行时决定 通过继承和虚函数来实现

注意:Derived objD; // 注意:会调用基类构造函数:基类执行Base(0),所以将ObjB.a置为0

虚函数 有了虚函数才能实现动态联编

4.3 虚函数 1. 虚函数的定义及使用 1) 定义: C++通过虚函数实现运行时的多态性。 声明虚函数的方法是在基类中的成员函数原型前加上关键字virtual。其格式如下: class 类名 { …… virtual 类型 成员函数名(参数表); …… };

改造(who()加上virtual) 派生类对象的地址赋值给指向基类的指针

[说明]: 必须首先在基类中声明虚函数。 派生类中与基类虚函数原型完全相同(包括函数名、返回类型、参数个数和参数类型的顺序)的成员函数,即使在说明时前面没有冠以关键字virtual也自动成为虚函数。 只有非静态成员函数可以声明为虚函数。

不允许在派生类中定义与基类虚函数名字及参数特征都相同,仅仅返回类型不同的成员函数。 仅仅函数名相同但参数特征不同的函数,系统视为不同的函数(即函数的重载),但派生类会屏蔽基类的方法。 通过声明虚函数来使用C++提供的多态性机制时,派生类应该从它的基类公有派生。

#include <iostream #include <iostream.h> class A { public: A() { cout<<"A构造 ";foo();} // 在这里,无论如何都是A::foo()被调用! virtual ~A() { cout<<"A析构 ";foo();} // 同上,无论如何都是A::foo()被调用! virtual void foo(){cout<<"A foo \n";}; }; class B: public A void foo(int i){ foo(), cout<<“B foo \n”;}; //因为屏蔽,foo()不存在,err! 不能直接访问,只能A::foo()访问 B() {cout<<“B构造 ”; foo();} //因为屏蔽,foo()不存在,err! virtual ~B() { cout<<"B析构 ";foo();} //因为屏蔽,foo()不存在, err! void main() A * p = new B; delete p; }

2) 虚函数的使用三要素 在基类定义中,必须把成员函数定义为虚函数。 若仅仅是返回类型的不同,则C++认为是错误的;如果是函数原型不同,仅函数名相同,则C++认为是一般的函数重载。 2) 虚函数的使用三要素 在基类定义中,必须把成员函数定义为虚函数。 在public派生类定义中,对虚函数的更新定义只能修改函数体内容,而函数名、返回类型、参数个数、参数类型及参数顺序必须与基类的定义完全相同。 必须用指向基类的指针(或引用)访问虚函数。 尽管可以像调用其他成员函数那样显式地用对象名来调用一个虚函数,但只有在同一个指向基类的指针(或引用)访问虚函数时,运行时的多态性才能实现。

[例]: class A { public: virtual void show() { cout<<“AAA…”<<endl; } //必须有virtual,否则不是虚函数 }; class B : public A void show() {cout<<“BBB…”<<endl;} //派生类中即使在说明时前面没有冠以关键字virtual也自动成为虚函数。

Figure called! Rectangle called! Triangle called!

因为使用普通对象调用虚函数时,系统仍然以静态联编方式完成对虚函数的调用。 void disp( A *p ) { p-> show( ); } main() { A *pa = new A; B *pb = new B; disp( pa ); disp( pb ); B tb; A t = tb; t.show(); delete pa; delete pb; } 或者用“ void disp( A &p )”, 但不可用“ void disp( A a)” 因为使用普通对象调用虚函数时,系统仍然以静态联编方式完成对虚函数的调用。

虚函数的限制 构造函数不能是虚函数,因为构造时对象还是一片未定型的空间。只有在构造完成后,对象才能成为一个类的名副其实的实例。 析构函数可以是虚函数,而且通常说明为虚函数。将析构函数定义为虚函数的目的在于:使用delete运算符删除一个对象时,能确保析构函数被正确的执行。这是因为,设置虚析构函数后,可以利用动态联编方式选择析构函数。

在这个例子中,在执行delete a的时候,实际上只有A::~A()被调用了,而B类的析构函数并没有被调用! 如果将上面A::~A()改为virtual,就可以保证B::~B()也在delete a的时候被调用了。因此基类的析构函数都必须是virtual的。

C++中虚函数的实现 使用虚函数,系统要有一定的空间开销,当一个类带有虚函数时,编译系统就会为该类构造一个虚函数表(virtual function table,简称 V-Table )。它是一个指针数组,存放每个虚函数的入口地址,系统在进行多态关联时的时间开销很少。因此,多态是高效的。

1-private的虚函数能否产生多态? 虚函数使用技巧 class A { public: void foo() { bar();} private: virtual void bar() { cout<<"a\n"; } }; class B: public A virtual void bar() { cout<<"B\n";} void main() A *p=new B; p->foo(); } 在这个例子中,虽然bar()在A类中是private的,但是仍然可以出现在派生类中,并仍然可以与public或者protected的虚函数一样产生多态的效果。并不会因为它是private的,就发生A::foo()不能访问B::bar()的情况,也不会发生B::bar()对A::bar()的override不起作用的情况。 这种写法的语意是:A告诉B,你最好override我的bar()函数,但是你不要管它如何使用,也不要自己调用这个函数。 P指向B 执行B::bar ,动态联编

2-构造函数和析构函数中的调用虚函数能否产生多态? 一个类的虚函数在它自己类的构造函数和析构函数中被调用的时候,它们就变成普通函数了,不“虚”了。也就是说不能在构造函数和析构函数中让自己类的虚函数自己“多态”。例如:

在new B的时候,基类A的构造函数被调用,但是在A的构造函数中,被调用的是A::foo()而不是B::foo()。 如果你希望delete p的时候,B::foo()被调用,那么你就错了(因为析构不虚)。 将~A() 为virtual ~A(),结果如下:

虚函数的限制 假定基类中有虚函数vf2(),在派生类中声明的成员函数vf2(int)【与基类中的虚函数vf2()名字相同,但参数不同】,vf2(int)是一般的函数重载,屏蔽了基类中的虚函数。

#include <iostream #include <iostream.h> class A { public: A() { cout<<"A构造 ";foo();} // 在这里,无论如何都是A::foo()被调用! virtual ~A() { cout<<"A析构 ";foo();} // 同上,无论如何都是A::foo()被调用! virtual void foo(){cout<<"A foo \n";}; }; class B: public A void foo(int i){ foo(), cout<<“B foo \n”;}; //因为屏蔽,foo()不存在,err! 不能直接访问,只能A::foo()访问 B() {cout<<“B构造 ”; foo();} //因为屏蔽,foo()不存在,err! virtual ~B() { cout<<"B析构 ";foo();} //因为屏蔽,foo()不存在, err! void main() A * p = new B; delete p; }

1. 纯虚函数 为了强制派生类重新定义其基类的虚函数,C++语言引入了纯虚函数(Pure Virtual Function)的概念。 一般格式: virtual 函数原型=0; [例]: virtual void ShowArea()=0;

具有纯虚函数的类称为?

运算符的重载形式 运算符的重载形式有以下两种: ① 运算符重载为类的成员函数 重载为类的成员函数; 重载为类的友元函数。 “operator”是定义运算符重载函数的关键字。 运算符的重载形式有以下两种: 重载为类的成员函数; 重载为类的友元函数。 ① 运算符重载为类的成员函数 运算符函数一般语法形式为: 函数类型 operator 运算符(形参表列) { 函数体; // 对运算符的重载处理 } “函数类型”指定了重载运算符的返回值类型,也就是运算结果类型。 “运算符”给定了要重载的运算符名称,必须是C++中可重载的运算符。 该成员函数必须放在类的public段

运算符重载的规则 [说明]: 重载时,运算符的优先级、结合性以及操作数的个数都不能改变。 由于重载不能改变运算符对象(操作数)的个数。 重载运算符不能有默认参数。因为如果有默认参数,则会改变运算符的参数个数。

加法运算符重载的方法和规则 #include <iostream> using namespace std; class Complex { public: Complex(double r=0.0, double i=0.0) { real=r; imag=i;} Complex Add(Complex &c); // 复数的加法运算 Complex Sub(Complex &c); // 复数的减法运算 void display(); // 复数输出 private: double real; double imag; };

Complex Complex::Add(Complex &c) { return Complex(real+c.real, imag+c.imag); } Complex Complex::Sub(Complex &c) return Complex(real-c.real, imag-c.imag); void Complex::display() cout<<"("<<real<<","<<imag<<")"<<endl;

友元函数重载运算符

1重载输入运算符(2) 几点注意事项: 该重载函数的返回类型是istream类对象的引用【 istream & operator>>】,返回引用的目的在于,把几个输入运算符>>放在同一条输入语句中时,该重载函数仍能正确工作。cin>>x>>y>>z; (cin>>x返回cin,再cin>>y,这两个cin必需是同一个对象,才能确保输入缓冲区的连续读取) (2)该运算符函数有两个参数,第一个参数是对istream类对象的引用,它出现在运算符>>的左边,第二个参数是出现在运算符右边的自定义类型对象。 (3)重载运算符函数operator>>的第二个参数必须是一个引用(因为输入的值要保存在该对象中)。

int main() { Point a(10,10); cin>>a; return 0; } 例exam12-6 输入运算符的重载 #include <iostream.h> class Point { public: Point(int i=0,int j=0) { x=i;y=j;} //<<、>>必须友元重载 friend istream &operator>>(istream &in,Point &a); private: int x,y; }; istream &operator>>(istream &in,Point &a) cout<<"Enter the x and y :"; in>>a.x; in>>a.y; return in; } int main() { Point a(10,10); cin>>a; return 0; }

2重载输出运算符 用友元函数重载输出运算符<<来实现用户自定义类型对象的输出。定义运算符函数的格式如下: ostream &operator<< (ostream &out, user-type &obj) { out<<obj.item1; 。。。。。 out<<obj.itemn; return out; } 与重载输出运算符函数一样,重载输出运算符也不能是成员函数法,但可以是该类的友元函数或独立函数。 user-type &obj 或user-type obj ,因为输出,不会改变obj

#include <iostream.h> class Point { public: Point(int i=0,int j=0) { x=i;y=j;} friend istream &operator>>(istream &in,Point &a); friend ostream &operator<<(ostream &out,Point &a); private: int x,y; }; istream &operator>>(istream &in,Point &a) cout<<"Enter the x and y :"; in>>a.x; in>>a.y; return in; }

ostream &operator<<(ostream &out,Point &a) { out<<a.x<<","<<a.y<<endl; return out; } int main() Point a(10,10); cout<<a; cin>>a; return 0;

5.1 模板的概念 [引例]: 可以看出,这些函数版本的功能都是相同的,只是参数类型和函数返回类型不同。 int max( int x, int y) { return (x>y) ? x: y; } double max( double x, double y) char max( char x, char y) 可以看出,这些函数版本的功能都是相同的,只是参数类型和函数返回类型不同。 那么能否为这些函数只写出一套代码呢? C++解决这个问题的一个方法就是使用模板。

函数模板 函数模板的定义和模板函数的生成 1) 定义函数模板的一般形式: template <class 类型参数名1, class 类型参数名 2, …> 函数返回值类型 函数名(形参表) {    //函数体 } 说明函数模板的关键字。 关键字class(或typename)后面的类型参数名是模板形参,它可以代表基本数据类型,也可以代表类。

函数模板

两种解决方法 1、强制类型转换 2、显示给出模板实参

第三种解决方法

字符串比较大小?

函数模板特化

vector push_back()可以将一个元素添加到容器的末尾

类模板的定义 定义类模板,包含两方面内容: 定义类; 在类定义体外定义体内未定义的成员函数。 1) 定义类的一般形式: template<class T1, class T2,……> //模板声明 class Name { … //类的定义 };

向量类模板定义 template<class T> class Vector { T *data; int size; public: Vector(int i) { data=new T[i]; } ~Vector() { delete[] data; } T &operator[](int i) { return data[i]; } };

既可以从类模板派生出类模板,也可以派生出普通类(非模板类)。主要有以下几种形式: 类模板的派生 既可以从类模板派生出类模板,也可以派生出普通类(非模板类)。主要有以下几种形式: 从类模板派生出类模板; 从类模板派生出普通类; 从普通类派生出类模板。

1) 从类模板派生出类模板 从类模板派生出新的类模板的格式如下所示: template <class T> class Base { …… }; template <class T > class Derived: public Base <T> 与定义一般派生类的格式类似,只是在指出它的父类时要加上模板参数。如,Base <T>。

2) 从类模板派生出普通类 从类模板派生出一个普通类的格式如下所示: template <class T> class Base { …… } ; class Derived: public Base<int> 首先,作为新派生出来的普通类的父类,必须是类模板实例化后生成的模板类。例如上面的Base<int>; 其次,在派生类定义之前不需要模板声明语句template <class T>。

template <class T> class Derived:public Base { T data; …… } 3) 从普通类派生出类模板 [例]: class Base { …… }; template <class T> class Derived:public Base { T data; …… }