C++面向对象程序设计计算机与信息学院罗宪第五章 多态性 (教材⑴ P252-P304)
本章主要内容:
多态性及有关概念
运行符重载
虚函数第一节 多态性及有关概念一、多态性
多态性:是指不同对象在收到相同的消息时,产生不同的动作 —— 用同一个名字定义不同的函数,执行不同但相似的操作 —— 实现“一个接口,多种方法”。
二,联编的概念及分类
1,联编的概念
联编:源程序经过编译,联接成可执行文件的过程 —— 即将可执行代码联编 ( 装配 ) 在一起的过程 。
2,联编的分类:
静态联编 ( 前期联编 ),在运行前完成的联编 —— 在编译时完成 ( 要求在编译时就知道调用函数的全部信息 ) —— 其优点是,效率高,。
第一节 多态性及有关概念
动态联编 ( 后期联编 ),在运行时才完成的联编 —— 在程序运行时动态调用所需函数 —— 优点是提供了更好的,灵活性,,问题的,抽象性,,程序的,易维护性,。
三,多态性的分类
编译时多态性:静态联编支持的多态性 ( 静态多态性 ) —
— 通过函数重载及运算符重载实现 。
运行时多态性:动态联编支持的多态性 ( 动态多态性 ) —
— 通过虚函数实现 。
四,函数重载
同一个类中的同名函数 —— 参数个数不一样,参数类型不一样,参数个数及类型不一样;
不同类中的同名函数 —— 通过类名调用或类的对象调用 。
同一个类中同名的普通成员函数及常量成员函数 —— 通过
const实现重载 。
第二节 运算符重载(教材① P253-285)
一,类以外的运算符重载例 【 5.1】 分析程序结果,熟悉在类外重载运算符 。
#include <iostream.h>
class complex
{ public,
double real ;
double imag;
complex(double r=0,double i=0)
{real=r;imag=i;}
};
complex operator+(complex co1,complex co2)
{complex temp;
temp.real=co1.real+co2.real;
temp.imag=co1.imag+co2.imag;
return temp;
}
第二节 运算符重载(教材① P253-285)
void main()
{ complex com1(1.1,2.2),com2(3.3,4.4),total1,total2;
total1=operator+(com1,com2);
cout <<"real1="<<total1.real;
cout<<" imag1="<<total1.imag<<endl;
total2=com1+com2;
cout <<"real2="<<total2.real;
cout<<" imag2="<<total2.imag<<endl;
}
程序中:
complex operator+(complex co1,complex co2)为运算符,+”的重载函数;
total1=operator+(com1,com2) ; total2=com1+com2为运算符重载的两种使用方法 。
第二节 运算符重载(教材① P253-285)
说明:
在 C++中运算符可以通过重载扩充运算符的功能;
运算符重载是通过 operator@( ) 实现的( @表示重载的运算符,+,-,*,/,>,<等) —— 操作符函数;
重载运算符不能改变运算符的原有的优先级和运算量的个数;
C++中大多数运算符均能够重载。如,+,-,*,/、
%,&,=,+=,-=,&&,||、!,++,[],new、
delete等;
不能重的运算符有:,.”,,.*”,,::”、“?:”、
,#”(预处理符) (教材③ P314);
第二节 运算符重载(教材① P253-285)
二、成员运算符函数
思考:将上例中的数据成员改为私有成员,程序可执行吗?
上例中的 complex类中的公有数据成员破坏了封装特性,如果将其改为私有成员,可以将运算符重载函数定义为成员函数。
㈠ 成员运算符函数定义
格式,<返回类型 > <类名 >::operator@(参数表 )
{ 函数体 }
其中,当为双目运算符时,参数表中为一个参数,单目运算符时,参数表为空 —— 通过 this指针传递。
第二节 运算符重载(教材① P253-285)
㈡ 双目运算符的重载
例 【 5.2】 重载二个复数对象的加、减、乘、除运算符 —— 熟悉双目运算符的重载。
1、分析:设两个复数 a+bi和 c+di,则有:
加法,(a+bi)+(c+di)=(a+c)+(b+d)i;
减法,(a+bi)-(c+di)=(a-c)+(b-d)i;
乘法,(a+bi)*(c+di)=(ac-bd)+(ad+bc)i
除法,(a+bi)/(c+di)=(a+bi)*(c-di)/(c2+d2)
=(ac+bd)/ (c2+d2)+(bc-ad)i/(c2+d2)
2、程序:
第二节 运算符重载(教材① P253-285)
#include <iostream.h>
class complex
{ private,
double real ;
double imag;
public,
complex(double r=0,double i=0)
{real=r;imag=i;}
void print();
complex operator+(complex c);
complex operator-(complex c);
complex operator*(complex c);
complex operator/(complex c);
};
第二节 运算符重载(教材① P253-285)
complex complex,:operator+(complex c)//重载,+”
{complex temp;
temp.real=real+c.real;
temp.imag=imag+c.imag;
return temp;
}
complex complex,:operator-(complex c)//重载,-”
{ complex temp;
temp.real=real-c.real;
temp.imag=imag-c.imag;
return temp;
}
第二节 运算符重载(教材① P253-285)
complex complex,:operator*(complex c)//重载,*”
{ complex temp;
temp.real=real*c.real-imag*c.imag;
temp.imag=real*c.imag+imag*c.real;
return temp;
}
complex complex,:operator/(complex c)//重载,/”
{complex temp;
double t;
t=c.real*c.real+c.imag*c.imag;
temp.real=(real*c.real+imag*c.imag)/t;
temp.imag=(c.real*imag-real*c.imag)/t;
return temp;
}
第二节 运算符重载(教材① P253-285)
void complex::print()
{cout <<real;
if (imag>0) cout<<"+";
if (imag!=0) cout <<imag<<"i"<<endl;
}
void main()
{ complex A1(2.3,4.6),A2(3.6,2.8),A3,A4,A5,A6;
A3=A1+A2;A4=A1-A2;
A5=A1*A2;A6=A1/A2;
A1.print();A2.print();
A3.print();A4.print();
A5.print();A6.print();
}
第二节 运算符重载(教材① P253-285)
双目运算符重载后的使用方法:
obj1@obj2 ; —— 隐式调用
obj1.operator@(obj2) ; —— 显示调用
㈢ 单目运算符重载
单目运算符重载时没有参数。
1、前置运算符重载第二节 运算符重载(教材① P253-285)
例 【 5.3】 单目运算符重载举例(前置 ++)
#include <iostream.h>
class coord
{ int x,y;
public,
coord(int i=0,int j=0);
void print();
coord operator ++();
};
coord::coord(int i,int j)
{x=i;y=j;}
void coord::print()
{ cout << "x="<<x<<" y="<<y<<endl;}
第二节 运算符重载(教材① P253-285)
coord coord::operator ++()
{
++x;//相当于,++this->x;
++y;//相当于,++this->y;
return *this;
}
void main()
{ coord ob(10,20);
ob.print();
++ob;
ob.print();
ob.operator++();
ob.print();
}
第二节 运算符重载(教材① P253-285)
重载单目运算符时,成员函数中没有参数传递,其参数是通过 this指针隐含传递给函数的。
重载单目运算符使用方法:
@obj ; —— 隐式调用
obj.operator@( ) —— 显式调用
2、前置运算与后置运算
例 【 5.4】 前置运算与后置运算举例第二节 运算符重载(教材① P253-285)
#include <iostream.h>
class coord
{ int x,y;
public,
coord(int i=0,int j=0);
void print();
coord operator ++();
coord operator ++(int);
coord operator +(coord c);
};
coord::coord(int i,int j)
{x=i;y=j;}
void coord::print()
{ cout << "x="<<x<<" y="<<y<<endl;}
第二节 运算符重载(教材① P253-285)
coord coord::operator ++() //重载前置运算符 ++{
++x;++y;
return *this;}
coord coord::operator ++(int)//重载后置运算符 ++{
x++;y++;
return *this;}
coord coord::operator +(coord c)//重载双目运算符 +{coord temp;
temp.x=x+c.x;temp.y=y+c.y;
return temp;}
第二节 运算符重载(教材① P253-285)
void main()
{ coord ob1(10,20),ob2(20,40),ob;
ob1.print();
ob2.print();
++ob1;
ob2++;
ob1.print();
ob2.print();
ob=(++ob1)+(ob2++);
ob.print();
}
即:后置运算在参数表中加一个 int即可 。
第二节 运算符重载(教材① P253-285)
㈣ 说明
运算符重载函数 operator@( )可以是任意类型 ——
通常与所操作的类的类型相同 ( 即用类名 ) ;
重载的运算符通常应保持其运算符的原意;
只能重载已有的运算符
当有多个相同的运算符重载函数时,C++根据参数的个数及类型决定调用哪个重载函数 。
三,友元运算符函数
在 C++中,友元函数主要用于运算符的重载 。
第二节 运算符重载(教材① P253-285)
㈠ 友元运算符重载的定义
friend <返回类型 > operator@(参数表 )
{ 函数体 }
友元函数不属于类的成员函数,无 this指针,当为双目运算符时,参数表中有二个参数,当为单目运算符时,参数表中有一个参数 。
㈡ 双目运算符重载
例 【 5.5】 将 例 【 5.2】 改为用友元函数重载 。
( 源程序文件 e506.cpp)
第二节 运算符重载(教材① P253-285)
#include <iostream.h>
class complex
{ private,
double real ;
double imag;
public,
complex(double r=0,double i=0)
{real=r;imag=i;}
void print();
friend complex operator+(complex a,complex b);
friend complex operator-(complex a,complex b);
friend complex operator*(complex a,complex b);
friend complex operator/(complex a,complex b);
};
第二节 运算符重载(教材① P253-285)
complex operator+(complex a,complex b)
{complex temp;
temp.real=a.real+b.real;
temp.imag=a.imag+b.imag;
return temp;
}
complex operator-(complex a,complex b)
{complex temp;
temp.real=a.real-b.real;
temp.imag=a.imag-b.imag;
return temp;
}
第二节 运算符重载(教材① P253-285)
complex operator*(complex a,complex b)
{complex temp;
temp.real=a.real*b.real-a.imag*b.imag;
temp.imag=a.real*b.imag+a.imag*b.real;
return temp;
}
complex operator/(complex a,complex b)
{complex temp;
double t;
t=b.real*b.real+b.imag*b.imag;
temp.real=(a.real*b.real+a.imag*b.imag)/t;
temp.imag=(b.real*a.imag-a.real*b.imag)/t;
return temp;
}
第二节 运算符重载(教材① P253-285)
void complex::print()
{cout <<real;
if (imag>0) cout<<"+";
if (imag!=0) cout <<imag<<"i"<<endl;
}
void main()
{ complex A1(2.3,4.6),A2(3.6,2.8),A3,A4,A5,A6;
A3=A1+A2; A4=A1-A2;
A5=A1*A2; A6=A1/A2;
A1.print(); A2.print();
A3.print(); A4.print();
A5.print(); A6.print();
}
第二节 运算符重载(教材① P253-285)
友元双目运行符重载函数的调用方法
obj1 @ obj2;
operator @ (obj1,obj2) ;
㈢ 单目运算符重载例 【 5.6】 用友元函数重载单目运算符,-”
#include <iostream.h>
class nclass
{ int a,b;
public:
nclass(int x=0,int y=0)
{a=x;b=y;}
friend nclass operator-(nclass obj);
void show();
};
第二节 运算符重载(教材① P253-285)
nclass operator-(nclass obj)
{obj.a=-obj.a;
obj.b=-obj.b;
return obj;
}
void nclass::show()
{cout<<"a="<<a<<" b="<<b<<endl;}
void main()
{nclass ob1(10,20),ob2;
ob2=-ob1;
ob1.show();
ob2.show();
}
第二节 运算符重载(教材① P253-285)
例 【 5.7】 将例 【 5.3】 用友元函数重载。( e507.cpp)
#include <iostream.h>
class coord
{ int x,y;
public,
coord(int i=0,int j=0);
void print();
friend coord operator ++(coord ob);
// friend coord operator ++(coord &ob);
};
coord::coord(int i,int j)
{x=i;y=j;}
void coord::print()
{ cout << "x="<<x<<" y="<<y<<endl;}
第二节 运算符重载(教材① P253-285)
coord operator ++(coord ob)
// coord operator ++(coord &ob)
{
++ob.x;
++ob.y;
return ob;
}
void main()
{ coord ob(10,20);
ob.print();
++ob;
ob.print();
operator++(ob);
ob.print();
}
第二节 运算符重载(教材① P253-285)
通过调试程序可知:
执行 ++ob1时 —— 即:执行 operator++(ob1)时,对象
ob1的成员的值并未增值 —— 值传送 —— 将形参
,coord ob”改为,coord &ob”,其结果便是正确的。
同成员函数重载一样,如果重载后置运算符,则需多加一个 int形参 —— 即将重载运算符函数原型改为:
friend coord operator ++(coord &ob,int);
友元运算符重载单目运算符使用方法:
@ aa ; —— 隐式调用
operator@(aa) ; —— 显式调用
注意点,运算符 =,( ),[ ],->不能用友元函数重载,
(只能用成员函数重载)
第二节 运算符重载(教材① P253-285)
四、成员运算符函数与友元运算符函数的比较
双目运算符:成员运算符只有一个参数( this指针表示运算符左边对象),友元函数带二个参数;
单目运算符:成员运算符不带参数( this指针表示对象本身),友元函数带一个参数;
通常,双目运算符用友元函数重载(除,=”等不能重载为友元的运算符外),单目运算符重载为成员函数。
当双目运算符左边为一个常数时,必须用友元运算符函数重载。 如程序:
第二节 运算符重载(教材① P253-285)
class nclass
{ private,
int a ;
int b ;
public,
nclass operator+(int x);

};
nclass nclass,,operator+(int x)
{ nclass temp ;
temp.a=a+x;
temp.b=b+x;
return temp;
}

void main()
{ nclass ob1,ob2;

ob2=ob1+100;

ob2=100+ob1;

}
程序中:
ob2=ob1+100;——
正确
ob2=100+ob1;——
不正确,为什么?
第二节 运算符重载(教材① P253-285)
结论,当双目运算符的运算分量有一个基本类型的数据时,最好用友元运算符重载。
例 【 5.8】 常数与复数相加。
#include <iostream.h>
class complex
{private,
double real ;
double imag;
public,
complex(double r=0,double i=0)
{real=r;imag=i;}
void show()
{cout <<"real="<<real<<" imag="<<imag<<endl;}
friend complex operator+(complex co,double x);
friend complex operator+(double x,complex co);
};
第二节 运算符重载(教材① P253-285)
complex operator+(complex co,double x){complex temp;
temp.real=co.real+x;temp.imag=co.imag;
return temp;
}complex operator+(double x,complex co)
{complex temp;temp.real=x+co.real;
temp.imag=co.imag;
return temp;}
void main(){ complex com1(1.1,2.2),com2,com3;
com2=com1+10.0;
com2.show();com3=20.0+com1;
com3.show();}
第二节 运算符重载(教材① P253-285)
五、赋值运算符,=”的重载
㈠ 拷贝构造函数的回顾
通常不用单独编写拷贝构造函数,直接使用默认拷贝构造函数 ;
默认拷贝构造函数是一种浅拷贝 —— 当在类中用 new
分配内存空间时,通常应编制相应的拷贝构造函数。
例:重新调试 e306程序,认识拷贝构造函数的使用。
㈡ 赋值运算符的重载
同拷贝构造函数一样,通常,=”不用重载,同一类的对象可直接赋值。如,ob1=ob2;
当在类中用 new分配内存空间,在析构函数中用 delete
中释放内存空间时,应重载,=”运算符。
第二节 运算符重载(教材① P253-285)
例 【 5.9】 调试下列程序,分析出错的原因
#include <iostream.h>
#include <string.h>
class string
{ char *ptr;
public,
string(char *s)
{ptr=new char[strlen(s)+1];
strcpy(ptr,s);
}
~string()
{delete ptr;}
void print()
{cout <<ptr<<endl;}
};
void main()
{string p1("chen");
{string p2(" ");
p2=p1;
cout <<"p2,";
p2.print();
}
cout<<"p1,";
p1.print();
}
第二节 运算符重载(教材① P253-285)
出错原因分析:
ptr
动态空间 1
P1
ptr
动态空间 2
P2
(a) 执行 p2=p1前
ptr
P1
ptr
P2
(b) 执行 p2=p1后
ptr
P1
(c) p2释放后

第二节 运算符重载(教材① P253-285)
解决办法:重载,=”运算符
例 【 5.10】 将上例程序改为:
#include <iostream.h>
#include <string.h>
class string
{ char *ptr;
public,
string(char *s)
{ptr=new char[strlen(s)+1];
strcpy(ptr,s);
}
~string()
{delete []ptr;}
void print()
{cout <<ptr<<endl;}
string &operator=(const string&s);
};
第二节 运算符重载(教材① P253-285)
string &string::operator=(const string&s)//重载,=”运算符
{ if(this==&s) return *this; //当用,ob1=ob1;”时
delete []ptr; //首先释放被赋值对象的空间
ptr=new char[strlen(s.ptr)+1];//重新为被赋值对象分配空间
strcpy(ptr,s.ptr);
return *this;
}
void main()
{string p1("chen");
{string p2(" ");
p2=p1;
p2.print();
}
p1.print();
}
第二节 运算符重载(教材① P253-285)
说明:
类的赋值运算符,=”只能重载为成员函数,重载后不能被继承 。
在重载,=”运算符时,应包括所有成员。
六、函数调用运算符,( )”与下标运算符,[ ]”的重载
(教材① P275-279—— 自学)
七、类的类型转换 (补充)
㈠ 系统预定义类型间的转换
1、隐式类型转换( C,C++)
赋值时自动转换;
char,short型与 int型运算时前者自动转换为整型;
当各种类型混合运算时,自动向高级别的类型转换 。
第二节 运算符重载(教材① P253-285)
2,显式类型转换
强制类型转换 ( C,C++),( 类型名 ) 表达式
函数法 ( C++),类型列 ( 表达式 )
㈡ 类类型与系统预定义类型间的转换
类类型与系统预定义类型间的转换通常有三种方法:
构造函数、类类型转换函数、运算符重载。
1、通过构造函数进行类型转换例 【 5.11】 分析程序第二节 运算符重载
(类的类型转换 —— 补充)
#include <iostream.h>
class example
{ private,
int num;
public,
example(int n)
{num=n;}
void print()
{cout <<"num="<<num<<endl;}
};
void main()
{example ob1=example(3);
ob1.print();
example ob2=6;
ob2.print();
}
第二节 运算符重载
(类的类型转换 —— 补充)
2、通过类型转换函数进行类型转换
通过类型转换函数可以实现将类类型转换为基本类型
例 【 5.12】 分析程序
#include <iostream.h>class complex
{ public,double real ;
double imag;
complex(double r=0,double i=0){real=r;imag=i;}
operator float(){return float(real);}
operator int()
{return int(real);}void print()
{cout<<"real="<<real<<" imag="<<imag<<endl;}};
第二节 运算符重载
(类的类型转换 —— 补充)
void main(){ complex com(2.6,4.2);
com.print();
cout<<1.2*float(com)<<endl;
cout<<5*int(com)<<endl;
}
类型转换函数的定义格式:
operator type( )
{ … }
—— 只能定义为成员函数
3、通过运算符重载实现类型转换 —— 如例 [5.8]中的一个基本类型的数据与复数对象相加:
第三节 虚函数 (教材① P285-303)
函数的重载及运算符的重载实现的是静态联编 —— 编译时多态性,如果要实现动态联编 —— 运行时多态性,
可用虚函数。
一、回顾 —— 引入派生类后的对象指针
由派生类与基类的赋值规则可知,基类与派生类的指针是相关的。
例 【 5.13】 分析程序的输出结果。
#include <iostream.h>class my_base
{ int a,b;
public,
my_base(int x,int y)
{a=x;b=y;}void show()
{cout <<"my_base--------\n";
cout <<"a="<<a<<" b="<<b<<endl;
}
};class my_derive:public my_base
{ int c;
public:
my_derive(int x,int y,int z):my_base(x,y)
{c=z;}void show()
{ cout <<"my_derive--------\n";
cout <<"c="<<c<<endl;
}
};
第三节 虚函数 (教材① P285-303)
void main(){my_base mb(50,50),*mp;
my_derive md(10,20,30);
mp=&mb;
mp->show();
mp=&md;mp->show();
((my_derive*)mp)->show();
}
输出结果为,my_base--------a=50 b=50
my_base--------
a=10 b=20
my_derive--------
c=30
第三节 虚函数 (教材① P285-303)
由程序的输出结果可知(即赋值规则):
声明为指向基类对象的指针可以指向公有派生的对象,但不允许指向私有派生的对象;
声明为指向派生类对象的指针不能指向基类对象;
声明为指向基类对象的指针指向公有派生的对象时,只能访问由基类继承的成员,不能直接访问公有派生类的成员 —— 因为它为静态联编;
若用指向基类的指针访问派生类的成员,可用显示类型转换为派生类的指针。如程序中的
((my_derive*)mp)->show() ;
第三节 虚函数 (教材① P285-303)
二、虚函数的定义及使用
问题:能否直接用指向基类对象的指针动态调用公有派生类的成员函数呢?
解决办法:用虚函数。
㈠ 虚函数及作用
例 【 5.14】 分析程序的输出结果,初步熟悉虚函数的应用。
#include <iostream.h>class my_base
{ int a,b;
public,
my_base(int x,int y)
{a=x;b=y;}virtual void show()
{cout <<"my_base--------\n";
cout <<"a="<<a<<" b="<<b<<endl;
}
};class my_derive:public my_base
{ int c;
public:
my_derive(int x,int y,int z):my_base(x,y)
{c=z;}virtual void show()
{ cout <<"my_derive--------\n";
cout <<"c="<<c<<endl;
}
};
第三节 虚函数 (教材① P285-303)
void main(){my_base mb(50,50),*mp;
my_derive md(10,20,30);
mp=&mb;
mp->show();
mp=&md;mp->show();
((my_derive*)mp)->show();
}
输出结果为:
my_base--------a=50 b=50
my_derive--------
c=30
第三节 虚函数 (教材① P285-303)
程序中,virtual 表示所定义的函数为虚函数 —— 执行 mp->show时,根据 mp所指向的具体对象,调用该对象的成员函数 —— 在执行时才进行动态联编。
结论,虚函数与派生类相结合,使 C++能支持运行时多态性 —— 实现在基类中定义派生类所拥有的通用
,接口,,而在派生类中定义具体的实现方法,即
,一个接口,多种方法,。
㈡ 虚函数的定义
在基类中:定义函数时用 virtual ;
在派生类中:函数的返回类型、函数名、参数的个数、
参数类型及顺序必须与基类中的原型完全相同。
第三节 虚函数 (教材① P285-303)
例 【 5.15】 分析程序的输出结果
#include <iostream.h>
class parent
{protected,
char version;
public:
parent()
{version='A';}
virtual void print()
{cout <<"\n The parent,version "<<version;}
};
第三节 虚函数 (教材① P285-303)
class derived1:public parent
{private:
int info;
public:
derived1(int number)
{info=number;
version='1';}
void print()
{cout<<"\n The derived 1 info:"<<info<<" version "<<version;}
};
class derived2:public parent
{private:
int info;
public:
derived2(int number)
{info=number; }
void print()
{cout<<"\n The derived 2 info:"<<info<<" version "<<version;}
};
第三节 虚函数 (教材① P285-303)
说明:
在派生类中定义虚函数时,virtual可用也可不用 —— 最好都使用;
虚函数在派生类中重新定义时,其原型必须与基类中相同;
虚函数在执行时,根据指针所指向的对象动态调用 —— 动态多态性;
虚函数也可用普通成员函数的调用方法(即用圆点运算符) —— 为静态调用;
必须用基类指针访问虚函数才能实现运行时的多态性;
虚函数在自身类中必须声明为成员函数(不能为友元函数或静态成员函数),但在另一个类中可以声明为友元函数;
第三节 虚函数 (教材① P285-303)
虚函数可以公有继承多次,其虚函数的特性不变;
构造函数不能定义为虚函数,但析构函数可以定义为虚函数。
㈢ 虚函数与重载函数的关系
普通函数重载是通过参数类型或参数的个数不同实现的,函数的返回类型可以不同;
重载一个虚函数时,其函数原型(返回类型、参数个数、类型及顺序)完全相同;
当重载的虚函数只有返回类型不同时,系统将给出错误信息;
如果只有函数名相同,参数个数或类型等不同时,
则为普通函数重载。(如教材① P288例 5.11)
第三节 虚函数 (教材① P285-303)
㈣ 多重继承与虚函数
多重继承的虚函数调用与单继承相似。
例 【 5.16】 分析程序的输出结果,了解多重继承虚函数的使用。 #include <iostream.h>
class base1
{ public:
virtual void fun()
{cout <<"----base1----"<<endl;}
};
class base2
{ public:
virtual void fun()
{cout <<"----base2----"<<endl;}
};
第三节 虚函数 (教材① P285-303)
class derived:public base1,public base2
{ public,
void fun()
{cout <<"----derived----"<<endl;}
};
void main()
{ base1 ob1,*p1;
base2 ob2,*p2;
derived ob3;
p1=&ob1; p1->fun();
p2=&ob2; p2->fun();
p1=&ob3; p1->fun();
p2=&ob3; p2->fun();
}
第三节 虚函数 (教材① P285-303)
例 【 5.17】 虚函数应用举例 —— 计算三角形、矩形和圆的面积
#include <iostream.h>
class figure
{ protected,
double x,y;
public,
figure(double a,double b)
{x=a;y=b;}
virtual void show_area( )
{ cout <<"No area computation defined";
cout <<" for this class.\n";
}
};
class triangle:public figure
{ public:
triangle(double a,double b):figure(a,b)
{ } ;
void show_area()
{ cout<<"Triangle with height "<<x;
cout<<" and base "<<y<<" has an area of";
cout<<0.5*x*y<<endl;}
};
class square:public figure
{ public,
square(double a,double b):figure(a,b)
{ };
void show_area()
{ cout <<"Square with dimension "<<x;
cout <<" * "<<y<<" has an area of ";
cout <<x*y<<endl;
}
};
class circle:public figure{ public:
circle(double a):figure(a,a)
{ };
void show_area()
{cout <<"Circle with radius "<<x;cout <<" has an area of ";
cout <<x*x*3.141593<<endl;
}
};
void main(){figure *p;
triangle t(10.0,6.0);
square s(10.0,6.0);
circle c(10.0);
p=&t; //p指向 triangle对象 tp->show_area();//调用 triangle的 show_area()
p=&s; //p指向 square对象 s
p->show_area();//调用 square的 show_area()
p=&c; //p指向 circle对象 c
p->show_area();//调用 circle的 show_area()}
第三节 虚函数 (教材① P285-303)
三、纯虚函数与抽象类
㈠ 纯虚函数的概念及定义
1、纯虚函数的概念
在上例中,基类 figure表示的是一种抽象的,图形,
概念,并不表示任何图形,该类中的 show( )的定义:
virtual void show_area( ){ cout <<"No area computation defined";
cout <<" for this class.\n";
}
—— 没有任何意义,它只是为派生类提供了一个公共界面。可将此函数:
virtual void show_area( )=0; —— 称为纯虚函数第三节 虚函数 (教材① P285-303)
2、纯虚函数的定义格式
virtual 返回类型 func_name(参数表 )=0;
㈡ 抽象类
1、抽象类的概念
一个类中至少定义了一个纯虚函数 —— 称为抽象类。
2.说明:
抽象类只能作为其它类的基类,不能定义其对象 ——
抽象类中包含有没有功能的纯虚函数 —— 抽象类用来提供派生类的公共接口函数;
可以声明指向抽象类的指针或引用 —— 用于指向派生类而实现多态性;
如果在派生类中未重新说明纯虚函数,而派生类只是继承基类的纯虚函数,则派生类仍是纯虚函数。
第三节 虚函数 (教材① P285-303)
例 【 5.18】 应用举例 —— 求圆、圆的内切正方形及外切正方形的面积及周长
#include <iostream.h>#include <math.h>
class shape
{ protected,
double r;
public:shape(double x)
{r=x;}
virtual double area()=0;
virtual double perimeter()=0;
};
class circle:public shape{ public:
circle(double x):shape(x)
{ }
double area()
{return 3.14*r*r;}double perimeter()
{return 2*3.14*r;}
};
class in_square:public shape
{ public:in_square(double x):shape(x)
{ }
double area()
{return 2*r*r;}
double perimeter(){return 4*sqrt(2)*r;}
};
class ex_square:public shape{ public:
ex_square(double x):shape(x){ }
double area()
{return 4*r*r;}double perimeter()
{return 8*r;}};
void main()
{ shape *p;circle ob1(5.0);
in_square ob2(5.0);ex_square ob3(5);
p=&ob1;
cout <<"Circle,"<<" area="<<p->area();cout<<" perimeter="<<p->perimeter()<<endl;
p=&ob2;cout <<"Internal,"<<" area="<<p->area();
cout<<" perimeter="<<p->perimeter()<<endl;
p=&ob3;cout <<"External,"<<" area="<<p->area();
cout<<" perimeter="<<p->perimeter()<<endl;};
第三节 虚函数 (教材① P285-303)
四、虚析构函数(教材① P299-303)
( P299— P303)