null第5章 多态性第5章 多态性多态性是面向对象程序
设计
领导形象设计圆作业设计ao工艺污水处理厂设计附属工程施工组织设计清扫机器人结构设计
语言的又一重要特征,它是指不同对象接收到同一消息时会产生不同的行为。
多态则是处理类的层次结构之间,以及同一个类内部同名函数的关系问题。
简单地说,多态就是在同一个类或继承体系结构的基类与派生类中,用同名函数来实现各种不同的功能。5.1 静态绑定和动态绑定 5.1 静态绑定和动态绑定 多态性的实现
编译时多态性: ---静态绑定(连接)----在C++中的编译时多态性是通过函数重载和运算符重载实现的。
运行时多态性: ---动态绑定(连接)----在C++中的运行时多态性是通过继承和虚函数实现的。5.2 虚函数 5.2 虚函数 5.2.1 虚函数的意义
1、回顾:基类与派生类的赋值相容
派生类对象可以赋值给基类对象。
派生类对象的地址可以赋值给指向基类对象的指针。
派生类对象可以作为基类对象的引用。
赋值相容的问题:
不论哪种赋值方式,都只能通过基类对象(或基类对象的指针或引用)访问到派生类对象从基类中继承到的成员, 不能借此访问派生类定义的成员。
2、虚函数使得通过基类对象的指针或引用访问派生类定义的成员可以施行。5.2.1 虚函数的意义5.2.1 虚函数的意义【例5-1】 某公司有经理、销售员、小时工等多类人员。经理按周计算薪金;销售员每月底薪800元,然后加销售提成,每销售1件产品提取销售利润的5%;小时工按小时计算薪金。每类人员都有姓名和身份证号等数据。
为简化问题,把各类人员的共有信息抽象成基类Employee,其他人员则继承该类的功能。 null//Eg5-1.cpp
#include
#include
using namespace std;
class Employee{
public:
Employee(string Name ,string id){ name=Name; Id=id; }
string getName(){ return name; } //返回姓名
string getID(){ return Id; } //返回身份证号
float getSalary(){ return 0.0; } //返回薪水
void print(){ //输出姓名和身份证号
cout<<"姓名: "<print();
Employee &rM=m;
rM.print();
}5.2.1 虚函数的意义5.2.1 虚函数的意义例5-1程序的运行结果如下:
经理:刘大海 编号: NO0001 周工资: 128
姓名: 刘大海 编号: NO0001
姓名: 刘大海 编号: NO0001
输出的第2、3行表明,通过基类对象的指针和引用只访问到了在基类中定义的print函数。5.2.1 虚函数的意义5.2.1 虚函数的意义将基类Employee的print指定为虚函数,如下形式:
class Employee{
……
virtual void print(){ cout<<"姓名: "<f( );
}
D::f5.2.2 虚函数的特性 5.2.2 虚函数的特性 1 .一旦将某个成员函数声明为虚函数后,它在继承体系中就永远为虚函数了
【例5-2】 虚函数与派生类的关系。
#include
#include
using namespace std;
class A {
public:
void f(int i){cout<<"…A"<f(1); //调用A::f
pA=&b; pA->f(1); //调用A::f
pA=&c; pA->f(1); //调用A::f
pA=&d; pA->f(1); //调用A::f
}5.2.2 虚函数的特性 5.2.2 虚函数的特性 2 只有通过基类对象的指针和引用访问派生类对象的虚函数时,才能体现虚函数的特性。
【例5-3】 只能通过基类对象的指针和引用才能实现虚函数的特性。
//Eg5-3.cpp
#include
using namespace std;
class B{
public:
virtual void f(){ cout << "B::f"<f();
rB.f();
}
本程序的运行结果如下:第1行输出没有体现虚函数特征
B::f
D::f
D::f5.2.2 虚函数的特性 5.2.2 虚函数的特性 3.派生类中的虚函数要保持其虚特征,必须与基类虚函数的函数原型完全相同,否则就是普通的重载函数,与基类的虚函数无关。
5.2.2 虚函数的特性5.2.2 虚函数的特性4. 派生类通过从基类继承的成员函数调用虚函数时,将访问到派生类中的版本。
【例5-5】 派生类D的对象通过基类B的普通函数f调用派生类D中的虚函数g
//Eg5-5.cpp
#include
using namespace std;
class B{
public:
void f(){ g(); }
virtual void g(){ cout << "B::g"; }
};
class D : public B{
public:
void g(){ cout << "D::g"; }
};
void main(){
D d;
d.f();
}5.3虚析构函数 5.3虚析构函数 基类析构函数几乎总是为虚拟析构函数,因为:
假定使用delete和一个指向派生类的基类指针来销毁派生类对象,如果基类析构函数不为虚,就如一个普通成员函数,delete函数调用的就是基类析构函数。在通过基类对象的引用或指针调用派生类对象时,将致使对象析构不彻底!null【例5-7】 在非虚析构函数的情况下,通过基类指针对派生对象的析构是不彻底的。
#include
using namespace std;
class A{
public:
~A(){ cout<<"call A::~A()"<
要求
对教师党员的评价套管和固井爆破片与爆破装置仓库管理基本要求三甲医院都需要复审吗
,有兴趣的同学可以看教程5.5纯虚函数和抽象类 5.5纯虚函数和抽象类 5.5.1、纯虚函数的概念
仅定义函数原型而不定义其实现的虚函数,定义:
class X
{
virtual ret_type func_name (param) = 0;
}
5.5.2、抽象类的概念
抽象类:包含一个或多个纯虚函数的类。
抽象类不能实例化,即不能定义抽象类的一个对象5.5纯虚函数和抽象类5.5纯虚函数和抽象类【例5-9】 抽象图形类及其应用。
//Eg5-9.cpp
#include
using namespace std;
class Figure{
protected:
double x,y;
public:
void set(double i,double j){ x=i; y=j; }
virtual void area()=0; //纯虚函数
};
nullclass Triangle:public Figure{
public:
void area(){cout<<"三角形面积:"<area(); //L4
Figure &rF=t;
rF.set(20,20);
rF.area(); //L5
}5.5.3 抽象类的应用5.5.3 抽象类的应用【例5-10】 建立一个如右图所示图形类的继承层次结构。基类Shape是抽象类,通过它能够访问派生类Point、Circle、Cylinder的类名、面积、体积等内容。5.5.3 抽象类的应用5.5.3 抽象类的应用虚函数及抽象类的应用
多态
从外部看:同一方法(函数)作用不同对象时,导致不同行为发生
从内部看:单接口、多实现
好处
代码重用
软件功能局部的修改和替代
5.5.3 抽象类的应用5.5.3 抽象类的应用【例5-10】 建立一个如图5-7所示图形类的继承层次结构。基类Shape是抽象类,通过它能够访问派生类Point、Circle、Cylinder的类名、面积、体积等内容。pointcirclecylinder三种几何图形的成员三种几何图形的成员printName()
print()pointPoint()
setPoint()
getX()
getY()
x,yarea()
printName()
print()CircleCircle()
setRadius()
x,y,radiusarea()
volume()
printName()
print()CylinderCylinder()
setHeight()
getVolume
x,y,radius,height抽象出虚基类:Shape抽象出虚基类:ShapepointPoint()
setPoint()
getX()
getY()
X,yCircleCircle()
setRadius()
X,y,radiusarea()
volume()
printShapeName()
print()CylinderCylinder()
setHeight()
getVolume
X,y,radiusshapeShape只是概念上的几何图形,永远不会有称为shape的对象存在,它的存在只是为了提供point,circle,cylinder的公有接口。所以shape的成员函数定义为:
area(){return 0;}
volume(){return 0;}
printShapeName()=0;
Print()=0;Shape.hShape.h#ifndef SHAPE_H
#define SHAPE_H
#include
class Shape {
public:
virtual double area() const { return 0.0; }
virtual double volume() const { return 0.0; }
virtual void printShapeName() const = 0;
virtual void print() const = 0;
};
#endif
Shape.cppShape.cpp不需要此源文件,因为没有函数要定义Point.hPoint.h
#ifndef POINT1_H
#define POINT1_H
#include "shape.h"
class Point : public Shape {
public:
Point( int = 0, int = 0 );
void setPoint( int, int );
int getX() const { return x; }
int getY() const { return y; }
virtual void printShapeName() const
{ cout << "Point: "; }
virtual void print() const;
private:
int x, y;
};
#endif Point.cppPoint.cpp#include "point1.h"
Point::Point( int a, int b ) { setPoint( a, b ); }
void Point::setPoint( int a, int b )
{
x = a;
y = b;
}
void Point::print() const
{ cout << '[' << x << ", " << y << ']'; } Circle.hCircle.h
#ifndef CIRCLE1_H
#define CIRCLE1_H
#include "point1.h"
class Circle : public Point {
public:
Circle( double r = 0.0, int x = 0, int y = 0 );
void setRadius( double );
double getRadius() const;
virtual double area() const;
virtual void printShapeName() const { cout << "Circle: "; }
virtual void print() const;
private:
double radius; // radius of Circle
};
#endif Circle.cppCircle.cpp#include "circle1.h"
Circle::Circle( double r, int a, int b ) : Point( a, b )
{ setRadius( r ); }
void Circle::setRadius( double r ) { radius = r > 0 ? r : 0; }
double Circle::getRadius() const { return radius; }
double Circle::area() const
{ return 3.14159 * radius * radius; }
void Circle::print() const
{ Point::print(); cout << "; Radius = " << radius;
}
Cylinder.hCylinder.h#ifndef CYLINDR1_H
#define CYLINDR1_H
#include "circle1.h"
class Cylinder : public Circle {
public:
Cylinder( double h = 0.0, double r = 0.0,
int x = 0, int y = 0 );
void setHeight( double );
double getHeight();
virtual double area() const;
virtual double volume() const;
virtual void printShapeName() const {cout << "Cylinder: ";}
virtual void print() const;
private:
double height;
};
#endif Cylinder.cppCylinder.cpp#include "cylindr1.h"
Cylinder::Cylinder( double h, double r, int x, int y ) : Circle( r, x, y )
{ setHeight( h ); }
void Cylinder::setHeight( double h )
{ height = h > 0 ? h : 0; }
double Cylinder::getHeight() { return height; }
double Cylinder::area() const
{
return 2 * Circle::area() + 2 * 3.14159 * getRadius() * height;
}
double Cylinder::volume() const
{ return Circle::area() * height; }
void Cylinder::print() const
{
Circle::print();
cout << "; Height = " << height;
}
main.cppmain.cpp#include
#include
#include "shape.h"
#include "point.h"
#include "circle.h"
#include "cylindr.h"
void vpf( const Shape * );
void vrf( const Shape & ); main.cppmain.cppvoid vpf( const Shape *baseClassPtr )
{
baseClassPtr->printShapeName();
baseClassPtr->print();
cout << "\nArea = " << baseClassPtr->area()
<< "\nVolume = " << baseClassPtr->volume() << "\n\n";
}
void vrf( const Shape &baseClassRef )
{
baseClassRef.printShapeName();
baseClassRef.print();
cout << "\nArea = " << baseClassRef.area()
<< "\nVolume = " << baseClassRef.volume() << "\n\n";
} main.cppmain.cppint main()
{
cout << setiosflags( ios::fixed | ios::showpoint )
<< setprecision( 2 );
Point point( 7, 11 );
Circle circle( 3.5, 22, 8 );
Cylinder cylinder( 10, 3.3, 10, 10 );
point.printShapeName();
point.print();
cout << '\n';
circle.printShapeName();
circle.print();
cout << '\n';
cylinder.printShapeName();
cylinder.print();
cout << "\n\n"; main.cppmain.cppShape *arrayOfShapes[ 3 ];
arrayOfShapes[ 0 ] = &point;
arrayOfShapes[ 1 ] = &circle;
arrayOfShapes[ 2 ] = &cylinder;
cout << "Virtual function calls made off "
<< "base-class pointers\n";
for ( int i = 0; i < 3; i++ )
vpf( arrayOfShapes[ i ] );
cout << "Virtual function calls made off "
<< "base-class references\n";
for ( int j = 0; j < 3; j++ )
vrf( *arrayOfShapes[ j ] );
return 0;
} 5.7 编程实作5.7 编程实作在4.9节设计的课程体系继承结构中,设计了comFinal、Account、Chemistry三个类,这些类的相关头文件comFinal.h、account.h、chemistry.h,以及类的实现文件comFinal.cpp、account.cpp、chemistry.cpp,都保存在目录C:\course中。
现对4.9节的编程实作进行完善,将comFinal、Account、Chemistry中的成员函数show设计成虚函数,并设计一个访问该类继承结构的接口函数display ,此函数通过基类comFinal对象的指针,访问派生类Account、Chemistry 类对象的虚函数show 。下图是类的继承层次图。5.7 编程实作5.7 编程实作为comFinal继承结构设计虚函数show5.7 编程实作5.7 编程实作编程过程如下:
(1)打开4.9节建立在目录C:\course中的
工程
路基工程安全技术交底工程项目施工成本控制工程量增项单年度零星工程技术标正投影法基本原理
项目文件com_main.dsw。
(2)在类comFinal的成员函数show 声明前面加上限定词virtual。
class comFinal{
……
virtual void show();
};
null(3)改写主程序
编写访问本课程类继承结构的接口函数display 和主函数,为此可改写原来的主文件com_main.cpp,内容如下。
//com_main.cpp
#include "comFinl.h"
#include "Chemistry.h"
#include "Account.h"
#include
void display(comFinal *p){ p->show(); }
void main(){
comFinal a[3];
comFinal c("王十",78,78,76);
Account a1("张三星",98,78,97,67,87);
Chemistry c1("光红顺",89,76,34,56,78);
a[0]=c;
a[1]=a1;
a[2]=c1;
for(int i=0;i<3;i++)
display(&a[i]);
} null