面向对想程序设计7_第1页
面向对想程序设计7_第2页
面向对想程序设计7_第3页
面向对想程序设计7_第4页
面向对想程序设计7_第5页
已阅读5页,还剩164页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第六章第六章 多态性多态性语文、数学、英语、政治、语文、数学、英语、政治、物理、化学、生物物理、化学、生物多态性指,同一个消息被不同对象接收时,多态性指,同一个消息被不同对象接收时,产生不同结果,即实现同一接口,不同方法。产生不同结果,即实现同一接口,不同方法。高中生计计 算算平均成绩平均成绩多态性多态性多态性指,同一个消息被不同对象接收时,多态性指,同一个消息被不同对象接收时,产生不同结果,即实现同一接口,不同方法。产生不同结果,即实现同一接口,不同方法。计计 算算平均成绩平均成绩大学生高数、英语、计算机、高数、英语、计算机、线性代数线性代数多态性多态性多态 重载多态:通过调用相同名字的函数

2、,表现出不同的行为。运算符重载也是一种重载多态。 运行多态:通过基类的指针,调用不同派生类的同名函数,表现出不同的行为。许多面向对象程序设计的书籍中所说的多态性,就是这种多态。 模板多态,也称为参数多态:通过一个模板,得到不同的函数或不同的类。这些函数或者类具有不同的特性和不同的行为。 第六章 多态性 多态与虚函数 虚函数(安全编程:覆盖C+虚函数指针) 纯虚函数和抽象类 运算符重载 模板 函数模板 类模板本章要求 理解多态性的概念 掌握虚函数的原理及使用 掌握运算符重载 掌握模板程序设计第六章 多态性多态与虚函数多态与虚函数 虚函数(安全编程:覆盖C+虚函数指针) 纯虚函数和抽象类 运算符重

3、载 模板 函数模板 类模板实例几何形状几何形状椭圆椭圆圆圆三角形三角形矩形矩形正方形正方形6.1 多态与虚函数 赋值兼容规则(子类型):需要基类对象的任何地方都可以使用公有派生类的对象来替代. 替代包括: 派生类的对象可以赋值给基类对象. 派生类的对象可以初始化基类的引用. 派生类的地址可以赋给指向基类的指针.A a,*p;B b;A &g=b;a=b;P=&b;class A public: void f(); void g();class B:public A public: void h(); 虚函数 虚函数是成员函数而且是非static的成员函数。说明虚函数的方法如下:

4、 virtual 类型说明 函数说明(参数表) 一旦在基类中指定某成员为虚函数,那么不管在派生类中是否给出virtual声明,派生类(以及派生类的派生类,依次类推)中对其重定义的成员函数均为虚函数。虚函数 重定义指对派生类中定义的成员函数,其重定义指对派生类中定义的成员函数,其函数名、参数个数和类型以及返回值类型函数名、参数个数和类型以及返回值类型与基类的虚成员函数相同。与基类的虚成员函数相同。(当基类虚函数返回值类型是某个类或其指针或引用时,派生类中重定义的成员函数的返回值也可以是子类型,vc+6.0不支持)。静态联编和动态联编 联编:将一个函数的调用与相应的函数体的代码相连接称为函数联编。

5、 静态联编:在编译根据CShape* pShape6的静态类型类决定pShapei-display()属于哪一个类。由于pShapei 的静态类型是CShape* ,所以确定display()是CShape: display. 动态联编:在运行时,根据pShapei实际指向的对象类型来确定display()属于哪一个类。C+规定动态联编是在虚函数的支持下实现的。o 使用虚函数的注意事项使用虚函数的注意事项n 只有类的成员函数才能说明为虚函数只有类的成员函数才能说明为虚函数n 静态成员函数不能是虚函数静态成员函数不能是虚函数n 内联内联(inline)函数不能是虚函数函数不能是虚函数n 构造函数

6、不能是虚函数构造函数不能是虚函数n 析构函数可以是虚函数析构函数可以是虚函数,而且通常声明为虚而且通常声明为虚函数函数参见工程参见工程0106虚函数虚析构函数 如果用动态创建的派生类对象的地址初始化基类的指针,创建的过程不会有问题:仍然是先调用基类构造函数,再执行派生类构造函数。 但是,在用delete运算符删除这个指针的时候,由于指针是指向基类的,通过静态联编,只会调用基类的析构函数。 为了解决派生类对象释放不彻底的问题,必须将基类的析构函数定义为虚析构函数。格式是在析构函数的名字前添加virtual关键字。函数原型如下:virtual CShape(); 此时,无论派生类析构函数是不是用v

7、irtual来说明,也都是虚析构函数。 再用delete shape来释放基类指针时,就会通过动态联编调用派生类的析构函数。虚析构函数深入理解多态性o 什么是多态性什么是多态性n 不同对象收到相同消息时产生不同的动作不同对象收到相同消息时产生不同的动作n 多态性的实现与函数联编有关多态性的实现与函数联编有关o 将一个函数的调用与相应的函数体的代码将一个函数的调用与相应的函数体的代码相连接称为函数联编相连接称为函数联编深入理解多态性o 编译时的多态性编译时的多态性n 静态联编所支持的多态性称为编译时的多态静态联编所支持的多态性称为编译时的多态性性n 函数重载、运算符重载、参数多态函数重载、运算符

8、重载、参数多态o 运行时的多态性运行时的多态性n 动态联编所支持的多态性称为运行时的多态动态联编所支持的多态性称为运行时的多态性性n 通过虚函数的继承实现通过虚函数的继承实现void 叫某人来吃饭(人叫某人来吃饭(人 p) p.吃饭();吃饭();深入理解多态性 应用程序不必为每个派生类编写功能调用,只要对基类进行处理即可以不变应万变,可以大大提高程序的可复用性。深入理解多态性class Employee virtual int GetSalary()=0;class sales:public Employee public: int GetSalary()/显示工资程序Employee *p

9、e = emFactory.GetEmployee(id);pe-GetSalary();class engineer:public Employee public: int GetSalary() 应用程序不必为每个派生类编写功能调用,只要对基类进行处理即可以不变应万变,可以大大提高程序的可复用性。 派生类的功能可以被基类的方法,指针或引用调用,这叫向后兼容,可以提高程序的扩充性和可维护性。深入理解多态性class Employee virtual int GetSalary()=0;class sales:public Employee public: int GetSalary()/显示

10、工资程序Employee *pe = emFactory.GetEmployee(id);pe-GetSalary();class engineer:public Employee public: int GetSalary()运行时的多态性需要满足三个条件:运行时的多态性需要满足三个条件:首先,类之间应该满足赋值兼容规则。首先,类之间应该满足赋值兼容规则。其二,要声明虚函数。其二,要声明虚函数。第三,要由第三,要由成员函数成员函数来调用或者通过来调用或者通过指针指针、引用引用来访问虚函数。来访问虚函数。如果使用对象名来访问虚函数,则联编在编如果使用对象名来访问虚函数,则联编在编译过程中就可以

11、进行,而无需在运行过程中译过程中就可以进行,而无需在运行过程中进行。进行。class Apublic: A() f(); A() virtual void f(); void g(); void h()f();g();class B:public Apublic: B() void f(); void g();A a;a.f();a.g();a.h();B b;b.f();b.g();b.h();A:A()和和A:fA:fA:gA:h ,A:f ,A:gB:B() ,A:A() ,A:fB:fB:gA:h ,B:f ,A:gclass Apublic: A() f(); A() virtual

12、 void f(); void g(); void h()f();g();class B:public Apublic: B() void f(); void g();A *p; A a;p=&a;p-f();p-g();p-h();A:fA:gA:h ,A:f ,A:gclass Apublic: A() f(); A() virtual void f(); void g(); void h()f();g();class B:public Apublic: B() void f(); void g();A *p; p=&b;p-f();p-A:f();p-g();p-h();

13、B:fA:fA:gA:h ,B:f ,A:gclass Apublic: A() f(); A() virtual void f(); void g(); void h()f();g();class B:public Apublic: B() void f(); void g();A *p; p=new B;delete p;B:B() ,A:A() ,A:fA:A() 基类构造函数中对虚函数的调用不采用动态基类构造函数中对虚函数的调用不采用动态绑定。绑定。注意class Employee virtual int GetSalary()=0;class sales:public Employe

14、e public: int GetSalary()/显示工资程序Employee *pe = emFactory.GetEmployee(id);pe-GetSalary();class engineer:public Employee public: int GetSalary()纯虚函数和抽象类 什么是纯虚函数 纯虚函数只有函数的声明,函数的定义必须在其派生类中完成 声明纯虚函数的类不能实例化 什么是抽象类 声明有纯虚函数的类称为抽象类 纯虚函数的声明virtual 返回值类型返回值类型 函数名函数名() = 0;参见工程参见工程0108class Employee virtual int

15、 GetSalary()=0;class sales:public Employee public: int GetSalary()/显示工资程序Employee *pe = emFactory.GetEmployee(id);pe-GetSalary();class engineer:public Employee public: int GetSalary() 抽象类的主要作用是通过它为一个类族建立一个公共的接口,使他们能够更有效的发挥多态特性。纯虚函数和抽象类class Employee virtual int GetSalary()=0;class sales:public Emplo

16、yee public: int GetSalary()/显示工资程序Employee *pe = emFactory.GetEmployee(id);pe-GetSalary();class engineer:public Employee public: int GetSalary() 抽象类的主要作用是通过它为一个类族建立一个公共的接口,使他们能够更有效的发挥多态特性。 抽象类派生出新类后,如果派生类没有给出全部纯虚函数的实现,这时的派生类仍然是一个抽象类。 抽象类不能实例化,但可以声明一个抽象类的指针和引用。通过指针或引用,我们就可以指向并访问派生类对象,进而访问派生类的成员,这种访问是

17、具有多态特征的。纯虚函数和抽象类class Employee virtual int GetSalary()=0;class sales:public Employee public: int GetSalary()/显示工资程序Employee *pe = emFactory.GetEmployee(id);pe-GetSalary();class engineer:public Employee public: int GetSalary()class B0public: virtual void display()=0;class B1:public B0public: void dis

18、play()cout“B1:display”;class D1:public B1public: void display()coutdisplay();void main()B0 *p; B1 b1; D1 d1; p=&b1;fun(p);/B1:display p=&d1;fun(p);/D1:display关于纯虚函数和抽象类的描述中,( )是错误的。A. 纯虚函数是一种特殊的虚函数,它没有具体的实现。B. 抽象类是指具有纯虚函数的类。C. 一个基类中说明有纯虚函数,该基类的派生类一定不再是抽象类。D. 抽象类只能作为基类来使用,其纯虚函数的实现由派生类给出。练习第六章

19、 多态性 多态与虚函数 虚函数(安全编程:覆盖C+虚函数指针) 纯虚函数和抽象类运算符重载运算符重载 模板 函数模板 类模板6.2 运算符重载 例:实现复数的表示及其加法操作。 C+语言没有提供复数类型。为了使程序中能够表示和处理复数,我们用一个复数类来实现它。 为了能实现复数加法操作,可以在复数类的定义中定义一个成员函数add,它把调用它的复数对象和参数指定的复数对象相加,返回相加之后得到的复数对象。class Complex double real,imag; public: Complex()real=0;imag=0; Complex(double r,double i)real=r;

20、imag=i; Complex add(const Complex &x) const Complex temp; temp.real=real+x.real; temp.imag=imag+x.imag; return temp; ;Complex a(1,2),b(3,4),c;c=a.add(b);Complex a(1,2),b(3,4),c;c=a+b;class Complex double real,imag; public: Complex()real=0;imag=0; Complex(double r,double i) real=r;imag=i;Complex

21、operator + (const Complex &x) const Complex temp; temp.real=real+x.real; temp.imag=imag+x.imag; return temp; ;Complex a(1,2),b(3,4),c;c=a+b;class Complexdouble real,imag; public: ;Complex add (const Complex &c1, const Complex &c2)Complex temp; temp.real=c1.real+c2.real; temp.imag=c1.imag

22、+c2.imag; return temp;friend Complex add (const Complex &c1, const Complex &c2)Complex a(1,2),b(3,4),c;c=add(a,b);class Complexdouble real,imag; public: ;Complex operator + (const Complex &c1, const Complex &c2)Complex temp; temp.real=c1.real+c2.real; temp.imag=c1.imag+c2.imag; retur

23、n temp;friend Complex operator + (const Complex &c1, const Complex &c2)Complex a(1,2),b(3,4),c;c=a+b;运算符重载 重载为类成员函数。 重载为全局(友元)函数。重载的两种形式性能提示:可以把一个运算符作为一个非成员、非友元函数重载。但是,这样的运算符函数访问类的private和protected数据时必须使用类的public接口中提供的函数,调用这些函数所涉及的开销会降低性能。可以将这些函数内联以提高性能。class Complex double real,imag; public

24、: Complex()real=0;imag=0; Complex(double r,double i) real=r;imag=i;Complex operator + (const Complex &x) const Complex temp; temp.real=real+x.real; temp.imag=imag+x.imag; return temp; ;class Complexdouble real,imag; public: ;Complex operator + (const Complex &c1, const Complex &c2)Comple

25、x temp; temp.real=c1.real+c2.real; temp.imag=c1.imag+c2.imag; return temp;friend Complex operator + (const Complex &c1, const Complex &c2) 定义形式函数类型函数类型 operator 运算符(形参)运算符(形参) . 重载为类成员函数时 参数个数=原操作数个数-1(后置+、-除外) 重载为全局(友元)函数时 参数个数=原操作数个数(后置+、-除外),且至少应该有一个自定义类型的形参。重载的两种形式作为成员函数重载 双目操作符重载函数的声明格式

26、class operator #(); 双目操作符重载函数的定义格式 :operator #()class Complex double real,imag; public: Complex()real=0;imag=0; Complex(double r,double i) real=r;imag=i;Complex operator + (const Complex &x) const Complex temp; temp.real=real+x.real; temp.imag=imag+x.imag; return temp; ; 双目操作符重载函数的使用格式 a; b;a#b;

27、或或a.operator#(b);作为成员函数重载class Complex double real,imag; public: Complex()real=0;imag=0; Complex(double r,double i) real=r;imag=i;Complex operator + (const Complex &x) const Complex temp; temp.real=real+x.real; temp.imag=imag+x.imag; return temp; ; 单目操作符重载函数的声明格式class operator #(); 单目操作符重载函数的定义格

28、式 :operator #()作为成员函数重载 单目操作符重载函数的使用格式 a;#a;或a.operator#(); 对于单目操作符,其重载函数是单目操作符的前置用法。而操作符+和有前置和后置两种用法。为了能够区分,则可以定义另一个带有int型参数的操作符+和的重载函数来表示它们的后置用法。作为成员函数重载int fun() static int a=4; return a;int& fun() static int a=4; return a; A fun() static A a; return a;A& fun() static A a; return a;class

29、A ;constclass Counter int value; public: Counter()value=0; Counter& operator +() /前置前置 value+; return *this; const Counter operator +(int) /后置后置 Counter temp=*this; +(*this); return temp;;作为全局(友元)函数重载 双目操作符重载函数的定义格式 operator #( , ) class Complexdouble real,imag; public: ;Complex operator + (cons

30、t Complex &c1, const Complex &c2)Complex temp; temp.real=c1.real+c2.real; temp.imag=c1.imag+c2.imag; return temp;friend Complex operator + (const Complex &c1, const Complex &c2)作为全局(友元)函数重载 使用格式 a; b;a#b或operator#(a,b)class Complexdouble real,imag; public: ;Complex operator + (const

31、Complex &c1, const Complex &c2)Complex temp; temp.real=c1.real+c2.real; temp.imag=c1.imag+c2.imag; return temp;friend Complex operator + (const Complex &c1, const Complex &c2) 单目操作符重载函数的定义格式 operator #( ) 作为全局(友元)函数重载 单目操作符重载函数的使用格式 a;#a;或operator#(a);作为全局(友元)函数重载 为了能够区分前置和后置+和的重载函数,

32、它们的后置用法: operator+(,int);作为全局(友元)函数重载实例 重载操作符+,使其能够实现实数与复数的混合运算。class Complex double real,imag; public: Complex()real=0;imag=0; Complex(double r,double i) real=r;imag=i; friend Complex operator + (const Complex &c1, const Complex &c2) friend Complex operator + (const Complex &c, double d

33、) friend Complex operator + (double d, const Complex &c)Complex a(1,2),b(3,4),c1,c2,c3;c1=a+b;c2=b+21.5c3=10.2+a;规则和限制 可以重载C+中除下列运算符外的所有运算符:. .* : ?: 只能重载C+语言中已有的运算符,不可臆造新的。 不改变原运算符的优先级和结合性。 不能改变操作数个数。规则和限制(续) 经重载的运算符,其操作数中至少应该有一个是自定义类型。 在重载运算符()、-或者=时,运算符重载函数必须声明为类的一个成员。对于其他的运算符,运算符重载函数可以是成员函数或

34、者友元函数. 但在某些情况下,操作符必须以全局函数来重载,才能满足使用上的要求。练习 如果在类对象a的类中重载运算符“+”,则a+3的显示调用( )。 A. a.operator(3) ; B. a-operator+(3); C. a.operator+(3) ; D. 3.operator+(a) ;赋值运算符的重载 两个同类对象之间可以赋值,其含义是用一两个同类对象之间可以赋值,其含义是用一个对象的状态来改变另一个对象的状态。个对象的状态来改变另一个对象的状态。 C+编译程序会为每个类定义一个隐式的赋编译程序会为每个类定义一个隐式的赋值操作符重载函数,其行为是逐个对成员进行赋值操作符重载

35、函数,其行为是逐个对成员进行赋值操作。值操作。 对象的成员中有动态的数据类型时,就不能对象的成员中有动态的数据类型时,就不能直接相互赋值,否则在程序的编译或执行过程中直接相互赋值,否则在程序的编译或执行过程中出现编译或运行错误。例如:出现编译或运行错误。例如:class Cdemoclass Cdemo public:public:CDemo(char CDemo(char * *s)s) ps = new charstrlen(s) + 1;ps = new charstrlen(s) + 1;strcpy(ps, s);strcpy(ps, s); CDemo()CDemo() if (p

36、s)if (ps) delete ps;delete ps; void print()void print() coutpsendl;coutpsendl; private:private: char char * *ps;ps;void main()void main() CDemo d1(Key), d2(Mouse);CDemo d1(Key), d2(Mouse);d1 = d2;d1 = d2; class CDemoclass CDemopublic:public:CDemo&CDemo& operator = (const CDemo &a) operat

37、or = (const CDemo &a) if (a.ps) ps = a.ps; if (a.ps) ps = a.ps; else ps = 0; else ps = 0; return return * *this;this; private: private: char char * *ps;ps;赋值运算符的重载d1d2keyd1 = d2*ps*ps栈空间栈空间堆空间堆空间Mouse 除了会产生与默认拷贝构造函数可能产生的类似问题以外,还会导致内存泄露。赋值运算符的重载class CDemoclass CDemopublic:public:CDemo&CDemo&

38、amp; operator = (const CDemo &a) operator = (const CDemo &a)if (ps) delete ps;if (ps) delete ps;if (a.ps)ps = new charstrlen(a.ps) + 1;if (a.ps)ps = new charstrlen(a.ps) + 1;strcpy(ps, a.ps);strcpy(ps, a.ps);else ps = 0;else ps = 0;return return * *this;this; private: private: char char * *p

39、s;ps;注意软件工程知识:当类的对象包含指向动态分配的内存的指针时,如果不为其提供重载的赋值运算符重载函数和拷贝构造函数会造成逻辑错误。软件工程知识:通常要把构造函数、析构函数、重载的赋值运算符以及拷贝构造函数一起提供给使用动态内存分配的类。注意软件工程知识:防止一个类对象赋值给另一个类对象是可以实现的,具体做法是将赋值操作声明为该对象的private成员。注意 拷贝构造函数和赋值操作符=重载函数区别: 创建一个对象时,用另一个已存在的同类对象对其初始化,调用拷贝构造函数。 对两个已存在的对象,通过赋值操作用其中一个对象来改变另一个对象的状态时,调用赋值操作符=重载函数。 例如:A a; A

40、 b=a; b=a; 调用拷贝构造函数,调用拷贝构造函数,等价于等价于A b(a);调用赋值操作符重载函数调用赋值操作符重载函数赋值运算符不能重载为友元函数,只能是赋值运算符不能重载为友元函数,只能是一个非静态成员函数。一个非静态成员函数。 并且它不能被派生并且它不能被派生类继承。类继承。注意重载转换运算符 在C+中,数据类型转换对于基本数据类型有两种方式: 1、隐式数据类转换 2、显式数据类型转换,也叫强制类型转换。 对于用户自定义的类,C+也提供了定义类型转换的机制,它通过带一个参数的构造函数和对类型转换操作符重载来实现一个类与其他类型之间的转换。 带一个参数的构造函数用作类型转换 可用作

41、从一个基本数据类型或其他类到一个类的转换。 参见工程 “转换运算符”。带一个参数的构造函数用作类型转换 自定义类型转换 在一个类中,可以对类型转换操作符进行重载,从而实现从一个类到一个基本数据类型或一个其他类的转换。 转换运算符声明形式 operator 类型名类型名 () ;自定义类型转换class RMB public: RMB(double value=0.0) yuan =value; fen = (value-yuan)*100+0.5; void ShowRMB() coutyuan “元元” fen 分分 endl; operator double () return yuan+

42、fen/100.0; private:int yuan, fen;void main() RMB r1(1.01),r2(2.20); RMB r3; /显式转换类型显式转换类型 r3 = (double)r1+(double)r2; r3.ShowRMB(); /自动转换类型自动转换类型 r3=r1+2.40; r3.ShowRMB(); /自动转换类型自动转换类型 r3 =2.0-r1; r3.ShowRMB(); 对于对于r3=r1+2.40;的系统工作的系统工作1、寻找重载的成员函数、寻找重载的成员函数+运算符运算符2、寻找重载的友元函数、寻找重载的友元函数+运算符运算符3、寻找转换运

43、算符、寻找转换运算符4、验证转换后的类型是否支持、验证转换后的类型是否支持+运算。运算。 转换运算符重载一般建议尽量少使用。转换运算符重载一般建议尽量少使用。分析 歧义问题:同时有带一个参数的构造函数和类型转换操作符重载函数,有时会产生歧义。 参见工程“转换运算符1” 解决: 显示类型转换 给带一个参数的构造函数加修饰符explicit,其含义是禁止把带一个参数的构造函数作为隐式类型转换符来用。歧义问题string类 , namespace std 可以初始化:string s1( hi ); 重载了 (as in cout =, , =, b?a:b;float max(float a, f

44、loat b)return ab?a:b;char max(char a, char b)return ab?a:b;template T max(T a,T b)return ab?a:b;函数模板的定义和使用 函数模板的定义 关键字class可以用typename取代,效果完全相同 type1,type2:类型参数,用来声明函数模板参数类型,局部变量和返回值类型 函数模板的使用template 函数定义函数定义函数名函数名(参数列表参数列表)例 求绝对值函数的模板#includetemplateT abs(T x) return x0?-x:x; void main() int n=-5;

45、 double d=-5.5; coutabs(n)endl; coutabs(d)endl;运行结果:55.5 分析分析 编译器从调用编译器从调用abs()时实参的类型,推导出时实参的类型,推导出函数模板的类型参数。例如,对于调用表达函数模板的类型参数。例如,对于调用表达式式abs(n),由于实参,由于实参n为为int型,所以推导出型,所以推导出模板中类型参数模板中类型参数T为为int。 当类型参数的含义确定后,编译器将以函数当类型参数的含义确定后,编译器将以函数模板为样板,生成一个函数:模板为样板,生成一个函数:int abs(int x) return x0?-x:x; 例 求绝对值函数

46、的模板 实际上,函数模板定义了一系列重载的函数,要使用函数模板所定义的函数(模板函数),首先必须对函数模板进行实例化(生成具体的函数)。函数模板的实例化函数模板的实例化通常是隐式的,即编译程序会根据调用时通常是隐式的,即编译程序会根据调用时实参的类型自动地把函数模板实例化为具实参的类型自动地把函数模板实例化为具体的函数体的函数。实例化函数模板-生成模板函数 有时编译程序无法根据调用时的实参类型确定所调用的模板函数,这时需要在程序中显示地实例化函数模板。 如 abs(n);显示实例化函数模板显示实例化函数模板template T1 sum(T2 a,T3 b)int i;long lng;Sum

47、(i,lng);int i;long lng;Sum(i,lng);errorint i;long lng;Sum(i,lng);显示实例化函数模板template T3 sum(T2 a,T1 b)int i;long lng;Sum(i,lng); errorint i;long lng;Sum(i,lng); OK 除了类型参数,模板也可带有非类型参数。如: template void f(T a) T tempsize; void main() f(1); 函数模板的非类型参数函数模板的非类型参数 非类型参数按常量对待 函数模板的形式参数表中除了使用参数化类型名以外,还可以使用确定类型

48、的参数。也就是说,函数模板的参数表中,一定要函数模板的参数表中,一定要包含参数化类型名,但不一定都使用参数包含参数化类型名,但不一定都使用参数化类型名。化类型名。还可以根据需要,使用确定类型的参数。如整型的参数、实型的参数,等等。 参见工程“模板”带有确定类型的参数的函数模板 程序中使用了C+所定义的typeid运算符。它可以在程序运行时,显示指定的数据的类型。使用的格式是: typeid(表达式).name() 或者typeid(类型标识符).name() typeid运算符RTTI(运行时类型识别) 通过使用RTTI,程序可以在运行时通过基类的指针或者引用来得到所指对象的实际类型。主要有两

49、个操作: typeid操作符:返回指针或者引用所指对象的实际类型。 dynamic_cast操作符:将基类类型的指针或引用安全地转换为派生类型的指针或者引用。 注意:只有当类中至少有一个虚函数时,才能返回我们所需的动态类型信息;否则,只能返回静态类型信息。 例 求绝对值函数的模板#includetemplateT abs(T x) return x0?-x:x; void main() int n=-5; double d=-5.5; coutabs(n)endl; coutabs(d)”。如果要用对象来作实参,相应的类中要对“”运算符进行重载。 用户定义的类取代类型参数 #include t

50、emplateT max_value(T x,T y,T z)T max_value(T x,T y,T z)/函数模板的定义:求x、y、z的最大值 T temp;if(xy) temp = x;else temp = y;if(ztemp) temp =z;return temp; void main() Circle C1(2,3,5),C2(3,5,8),C3(3,2,6); coutmax_value(12,32,21)endl;/用整数作实参调用函数模板coutmax_value(a,A,9)endl; /用字符作实参调用函数模板coutmax_value(C1,C2,C3)endl

51、;coutmax_value(C1,C2,C3)(Circle m2)int operator(Circle m2)/重载“”运算符if(radiusm2.radius)return 1; else return 0; private: int x,y; / 圆心座标double radius; / 圆半径; ostream &operator( ostream &out, Circle &C1 ) outx=C1.x y=C1.y; out radius=C1.radius; return out; class Circle /Circle类的定义friend ost

52、ream &operator( ostream &, Circle & );friend ostream &operator(Circle m2)int operator(Circle m2)/重载“”运算符if(radiusm2.radius)return 1; else return 0; private: int x,y; / 圆心座标double radius; / 圆半径; void main() Circle C1(2,3,5),C2(3,5,8),C3(3,2,6); coutmax_value(12,32,21)endl;/用整数作实参调用函数模

53、板coutmax_value(a,A,9)endl; /用字符作实参调用函数模板coutmax_value(C1,C2,C3)endl; /用对象作参数调用函数模板程序运行结果是:程序运行结果是:3232a ax=3 y=5 radius=8 函数模板与函数重载 为了弥补函数模板所缺乏的一些灵活性,需要把函数模板与函数重载结合起来使用。template T max(T a,T b)return ab?a:b;int x,y,z;double l,m,n;z=max(x,y);l=max(m,n);max(x,m);max(x,m);max(x,m);double max(int a,doubl

54、e b)return ab?a:b;函数模板与函数重载 可以定义相同名字但形参数目或类型不同的多个函数模板,也可以定义和函数模板有相同名字的普通非模板函数函数模板与函数重载 函数模板和非模板函数重载的情况下,一个具体的函数调用,就有多个函数可供选择。具体的选择是根据函数调用所提供的参数进行的。 首先寻找函数名和参数能精确匹配的非模板函数 如果没有找到,选择参数可以匹配的函数模板 如果还不成功,通过参数自动转换,选择非模板函数类模板 什么是类模板 类模板是用户为类定义的一种模式,使类中的某些数据成员的类型、成员函数的参数或返回值能够使用任意的数据类型类模板 类模版的定义 关键字class可以用t

55、ypename取代,效果完全相同 类型参数可以在成员函数和数据成员中作为数据类型使用template class ; 在类模板的外部定义类模板的成员函数时的格式: 首先要使用类模板的头部,以表明类模板定义了几个类型参数; 作用域运算符“:”前面的类名用类模板名代替,而且也要在尖括号中注明所有类模板的类型参数; 函数的参数表或者自动变量的定义,则是可以使用类型参数,也可以不使用。类模板类模板 在类外部定义成员函数的格式如下: template 返回值类型返回值类型 类模板名类模板名 : 成员函数名成员函数名( 参数表参数表 ) 函数体函数体template /类模板:实现对任意类型数据进行存取类

56、模板:实现对任意类型数据进行存取class Store private: T item; / 用于存放任意类型的数据用于存放任意类型的数据 int haveValue; / 用于标记用于标记item是否已被存是否已被存 入内入内容容 public: Store(); / 构造函数构造函数 T GetElem(); /提取数据函数提取数据函数 void PutElem(T x); /存入数据函数存入数据函数;/ 缺省构造函数的实现缺省构造函数的实现template Store:Store(): haveValue(0) / 提取数据函数的实现提取数据函数的实现template T Store:G

57、etElem() / 如果试图提取未初始化的数据,则终止程序如果试图提取未初始化的数据,则终止程序 if (haveValue = 0) cout No item present! endl; exit(1); return item; / 返回返回item中存放的数据中存放的数据 / 存入数据函数的实现存入数据函数的实现 template void Store:PutElem(T x) haveValue+; / 将将haveValue 置为置为 TRUE,表示,表示item中已存入中已存入数值数值 item = x; / 将将x值存入值存入item类模板的实例化 函数模板在调用它或用它对函

58、数指针进行初始化或赋值时实例化。类模板的实例化则是对象的实例化一起完成的,且需显示实例化且需显示实例化。 在类模板后面的尖括号中,表明取代类型参数的实际类型名; 再写对象名和构造对象所需要的实参数。 一般化的格式如下: 类模板名类模板名 对象名对象名(实参数实参数);例 类模板应用举例#include #include / 结构体Studentstruct Student int id; /学号 float gpa; /平均分; template /类模板:实现对任意类型数据进行存取类模板:实现对任意类型数据进行存取class Store private: T item; / 用于存放任意类型

59、的数据用于存放任意类型的数据 int haveValue; / 用于标记用于标记item是否已被存是否已被存 入内入内容容 public: Store(); / 构造函数构造函数 T GetElem(); /提取数据函数提取数据函数 void PutElem(T x); /存入数据函数存入数据函数;/ 缺省构造函数的实现缺省构造函数的实现template Store:Store(): haveValue(0) / 提取数据函数的实现提取数据函数的实现template T Store:GetElem() / 如果试图提取未初始化的数据,则终止程序如果试图提取未初始化的数据,则终止程序 if (h

60、aveValue = 0) cout No item present! endl; exit(1); return item; / 返回返回item中存放的数据中存放的数据 / 存入数据函数的实现存入数据函数的实现 template void Store:PutElem(T x) haveValue+; / 将将haveValue 置为置为 TRUE,表示,表示item中已存入中已存入数值数值 item = x; / 将将x值存入值存入itemvoid main(void) Student g= 1000, 23; Store S1, S2; Store S3; Store D; S1.PutElem(3); S2.PutElem(-7); cout S1.GetElem() S2.GetElem() endl; S3.PutElem(g); cout The student id is S3.GetElem().id endl;cout Retrieving object D ;cout D.GetElem() endl; /输出对象D的数据成员/ 由于D未经初始化,在执行函数D.Get

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论