虚函数和多态性
使用基类指针,示例代码如下(从Box派生两个类)
class Carton:public Box{
};
class ToughPack:public Box{
};
Carton aCarton(10.0,10.0,5.0);
Box * pBox=& aCarton;
ToughPack hardcase(12.0,8.0,4.0);
pBox=&hardcase;
有如下语句:pBox()->volume; 此时如pBox包含的是Carton的对象地址,那
么它指向的就是Carton对象的函数,同理hardcase也一样
调用继承的函数
头文件Box.h
#ifndef BOX_H
#define BOX_H
================ 基类=========================================
class Box{
//构造器
public:
Box(double lengthValue=1.0,double widthValue=1.0,double
heightValue=1.0);
//成员函数
void showVolume() const;
double volume() const;
//成员变量
protected:
double length;
double width;
double height;
};
#endif
==================================================================
实现文件Box.cpp
#include "Box.h"
#include <iostream>
using std::cout;
using std::endl;
//构造函数
Box::Box(double lvalue,double wvalue,double hvalue):length
(lvalue),width(wvalue),height(hvalue){}
void Box::showVolume() const{
cout<<"Box usable volume is "<<volume()<<endl;
}
double Box::volume() const{
return length*width*height;
}
=================派生类============================================
头文件ToughPack.h
#ifndef TOUGHPACK_H
#define TOUGHPACK_H
#include "Box.h"
class ToughPack:public Box{
public:
ToughPack(double lengthValue,double widthValue,double
heightValue);
double volume() const;
}
===================================================================
实现文件ToughPack.cpp
#include "ToughPack.h"
ToughPack::ToughPack(double lval,double wval,double hval):Box
(lval,wval,hval){}
double ToughPack::volume() const{
return 0.85*length*width*height;
}
====================================================================
使用继承的函数
#include <iostream>
#include "Box.h"
#include "ToughPack.h"
using std::cout;
using std::endl;
int main(){
Box myBox(20.0,30.0,40.0);
ToughPack hardcase(20.0,30.0,40.0);
cout<<endl;
myBox.showVolume();//调用基类函数
hardcase.showVolume();//调用派生类函数
return 0;
}
如果想直接调用ToughPack中的valume()方法,示例代码如下:
cout<<"hardcase volume is"<<hardcase.volume()<<endl;
Box *pBox =&hardcase;
cout<<"hardcase volume through pBox is"<<pBox->volume()<<endl;
虚函数
一个基类中如果定义了虚函数,那么在派生于这个类的任何类,该函数都是动态
绑定的.(virtual) 使用对象的引用或指针都可以调用虚函数
注: 每个派生类都可以执行虚函数的自有版本
示例代码
class Box {
//构造器
public:
Box(double lenthValue=1.0,double widthValue=1.0,double
heightValue=1.0);
void showVolume() const;
//虚函数
virtual double volume() const;
//成员变量
protected:
double length;
double width;
double lenght;
}
=================================================================
Carton.h头文件
#ifndef CARTON_H
#define CARTON_H
#include <string>
#include "Box.h"
using std::string;
class Carton:public Box{
//构造函数
pulbic:
Carton(double lv,double wv,double hv,string
material="Cardboard");
//副本构造成器
Carton(const Carton & aCarton);
//析构函数
double volume() const;
//成员变量
private:
string * pMaterial;
};
#endif
==========================================================
实现文件Carton.cpp
#include "Carton.h"
Carton::Carton(double lv,double wv,double hv,string material):Box
(lv,wv,hv){
pMaterial=new string(material);
}
Carton::Carton(const Carton & aCarton){
length=aCarton.width;
width=aCarton.height;
height=aCarton.width;
pMaterial=new string (* aCarton,pMaterial);
}
Carton::~Carton(){
delete pMaterial;
}
double Carton::volume() const{
double vol-(length-0.5)*(width-0.5)*(height-0.5);
return vol>0.0?vol:0.0;
}
=============================================================
调用访函数的示例代码如下:
#include <iostream>
#include "Box.h"
#include "ToughPack.h"
#include "Carton.h"
using std::cout;
using std::endl;
int main(){
Box myBox(20.0,30.0,40.0);
ToughPack hardcase(20.0,30.0,40.0);
Carton aCarton(20.0,30.0,40.0);
//显示三个对象的体积
cout<<endl;
myBox.showVolume();
hardcase.showVolume();
aCarton.showVolume();
cout<<endl;
pBox=& hardcase;
cout<<"hardcase volume through pBox is"<<pBox->volume()<<endl;
pBox->showVolume();
cout<<endl;
pBox=& aCarton;
cout<<"aCarton volume through pBox is"<<pBox->volume()<<endl;
pBox->showVolume();
return 0;
}
虚函数中的默认参数值
如果在基类的虚函数中使用默认参数,在通过基类指针调用该函数时,只会
使用基类中的默认参数
头文件Box.h
#ifndef BOX_H
#define BOX_H
class Box {
//构造器
public:
Box(double lenthValue=1.0,double widthValue=1.0,double
heightValue=1.0);
void showVolume() const;
//虚函数
virtual double volume(const int i=5) const;
//成员变量
protected:
double length;
double width;
double lenght;
};
#endif
====================================================================
实现文件Box.cpp
#include "Box.h"
#include <iostream>
using std::cout;
using std::endl;
//构造函数
Box::Box(double lvalue,double wvalue,double hvalue):length
(lvalue),width(wvalue),height(hvalue){}
void Box::showVolume() const{
cout<<"Box usable volume is "<<volume()<<endl;
}
//使用默认参数
double Box::volume(const int i) const{
cout<<"Parameter"<<i<<endl;
return length*width*height;
}
====================================================================
派生类 头文件ToughPack.h
#ifndef TOUGHPACK_H
#define TOUGHPACK_H
#include "Box.h"
class ToughPack:public Box{
//构造成器
public:
ToughPack(double lengthValue,double widthValue,double
heightValue);
//虚函数
virtual double volume(const int i=500) const;
};
#endif
===================================================================
实现文件ToughPack.cpp
#include "ToughPack.h"
#include <iostream>
using std::cout;
using std::endl;
ToughPack::ToughPack(double lVal,double wVal,double hVal):Box
(lVal,wVal,hVal){}
double ToughPack::volume() const{
cout<<"Parameter"<<i<<endl;
return 0.85*length*width*height;
}
=====================================================================
调用时
#include <iostream>
#include "Box.h"
#include "ToughPack.h"
#include "Carton.h"
using std::cout;
using std::endl;
int main(){
Box myBox(20.0,30.0,40.0);
ToughPack hardcase(20.0,30.0,40.0);
Carton aCarton(20.0,30.0,40.0);
cout<<endl;
myBox.showVolume();
hardcase.showVolume();
aCarton.showVolume();
cout<<endl;
cout<<"hardcase volume is"<<hardcase.volume()<<endl;
Box pBox=& myBox;
cout<<"myBox volume through pBox is"<<pBox->volume()<<endl;
pBox->showVolume();
cout<<endl;
pBox=& hardcase;
cout<<"hardcase volume through pBox is"<<pBox->volume()<<endl;
pBox->showVolume();
cout<<endl;
pBox=& aCarton;
cout<<"aCarton volume through pBox is"<<pBox->volume()<<endl;
pBox->showVolume();
return 0;
}
通过引用来来调用虚函数
对虚函数的引用
#include <iostream>
#include "Box.h"
#include "ToughPack.h"
#include "Carton.h"
using std::cout;
using std::endl;
void showVolume(const Box & rBox);
int main(){
Box myBox(20.0,30.0,40.0);
ToughPack hardcase(20.0,30.0,40.0);
Carton aCarton(20.0,30.0,40.0);
cout<<endl;
showVolume(myBox);
showVolume(hardcase);
showVolume(aCarton);
return 0;
}
void showVolume(const Box & rBox){
rBox.showVolume();
}
调用虚函数的基类版本 示例代码如下:
通过派生类对像的指针或引用虚函数的派生类版本
Carton aCarton(40.0,30.0,20.0)
Box* pBox=&aCarton;
计算Carton对像的总体积损失:
double difference=pBox->Box::volume()->pBox->volume();
pBox->Box::volume()
在指针和类对像之间的转换
如果程序包含派生类的指针,就可以把指针隐式转换为基类指针,(可以是直接基
类指针,也可以是间接基类指针)
例如:
Carton * pCarton=new Carton(30,40,10);
可以把这个隐式指针转换为Carton的直接基类指针,
Box * pBox=pCarton;
还可以把派生类的隐式指针转换为间接基类的指针,示例化码如下:
CerealPace继承Carton,carton继承Box
Box* pBox=pCerealPack;//把pCerealPack中的地址从“指向CerealPack”的指
针类型转换为"指向Box"的指针类型
如果要指定显示转换,可以使用static_case<>()运算符
Box* pBox=static_case<Box*>(pCerealPack);
动态强制转换 (只能应用于多态类类型的指针和引用,即至少包含一个虚函数的
类类型)
在使用期间,要指定动态强制转换,应使用dynamic_case<>()运算符
动态转换指针
动态制转换有两种类型:
一种是沿着类层次结构向下进行强制转换(从直接或间接基类的指针转换为派
生类型的指针,称为downcast)
示例代码如下:
目标类型 要强制转换为新类型的表达式
Carton * pCarton=dynamic_cast<Carton*> (pBox);
注:Carton和Box必须包含虚函数
另一种是跨类层次的强制转换,称为crosscast
Contents* pContents=dynamic_cast<Contents*>(pBox);
注: Contents和Box类都必须是多态性的
假定一个Box对象指向某个对象,如果想调用Carton类的一个非虚函数
基类指针只允许调用派生类的虚函数,而dynamic_cast<>()运算符可以调用
非虚函数
示例代码:如果想调用Carton类的一个非虚函数在员surface()方法
dynamic_cast<Carton*>(pBox)->surface();
注:这样作的前提是确保pBox指向Carton对象或指向Carton作为基类的对象
正确写法:
if(Carton* pCarton=dynamic_cast<Carton*>(pBox)){
pCarton->surface();
}
注:如果要强制转换的指针类型是const,则目标类型也必须是const,
如果把const指针转换为非const类型指针,就必须使用const_cast<>()
转换引用(函数的引用参数)
示例人代码如下:
double doThat(Box & rBox){
Carton& rCarton=dynamic_cast<Carton&>(rBox);
}
多态性的成本 ( 虚函数的调用需要额外和开销)
简单的说包含相同数据成员,含有虚函数的类要比没有的类占用的内存要多
内存使用多的原因:在创建多态类的对象,要在对象中创建一个特殊指针(用
于调用对象中的虚函数)
纯虚函数(类似于接口-所有的成员都有是抽象成员)
示例代码如下:
class Shape{
public:
virtual void draw() const=0;
virtual void move(const Point& newPosition)=0;
protected:
Point position;
Shape(const Point& shapePosition):position(shapePosition){}
};
抽象类(抽象类的指针或引用可以用作参数或返回类型)
抽象类的构造函数可以在派生类的构造函数的初始化列表中调用(一般为
protected类型)
示例代码:声明一个新类,它把shape类作为基类
class Circle:public Shape{
public :
Circle(Point center,double circleRadius):Shape(),radius
(circleRadius){}
virrual void draw() const{
cout<<"Circle center"<<position<<"radius"<<radius<<endl;
}
virrual void move(const Point& newCenter){
position=newCenter;
}
private:
double radius;
}
注: Circle类定义了draw()和move()函数, 如果没有定义这两个方法中的
任何一个,Circle就是一个构造类
如果类中有一个纯虚函数,该类就是抽象类,派生类必须定义基类中的每
个纯虚函数,否则派生类也是一个抽象类,当然抽象类还可以包含非纯虚函数
示例代码:
class Box{
public:
Box(double lengthValue=1.0,double widthValue=1.0,double
heightValue=1.0);
protected:
double length;
double width;
double height;
};
#include<iostream>
#include "Box.h"
#include "ToughPack.h"
#include "Carton.h"
using std::count;
using std::endl;
int main(){
cout<<endl;
ToughPack hardcase(20.0,30.0,40.0);
Box* pBox=&hardcase;
cout<<"Volume of hardcase is"<<pBox->volume()<<endl;
Carton aCarton(20.0,30.0,40.0);
pBox=&aCarton;
cout<<"volume of aCarton is"<<pBox->volume()<<endl;
return 0;
}
用接口的抽象类 (只有包含一个纯虚接口的抽象类可以用于定义标准类接口)
间接的抽象类
多级继承 示例代码
vessel为Box的基类
头文件如下
#ifndef VESSEL_H
#define VESSEL_H
class Vessel{
public:
virrual double volume() const=0;// 虚函数
};
#endif
Box类的头文件为:
#ifndef BOX_H
#define BOX_H
#include "Vessel.h"
class Box:public Vessel{
public:
Box(double lengthValue=1.0,double widthValue=1.0,double
heightValue=1.0);
virtual double volume() const;//不是纯虚状态
protected:
double length;
double width;
double height;
};
#endif
实现文件Box.cpp
double Box::volume() const{
return length * width* height;
}
另一个vessel中另一个派生类can,头文件can.h
#ifndef CAN_H
#define CAN_H
#inclued "Vessel.h"
class Can:pulbic Vessel{
public :
Can(double canDiameter,double canHeight);
virtual double volume() const;//
protected:
double diameter;
double height;
static const double pi;//虚函数
};
#endif
实现文件can.cpp
#include "Can.h"
Can::Can(double canDiameter,double canHeight):diameter
(canDiameter),height(canHaight){}
double Can:volume() const{
return pi* diameter*diameter*height/4;
}
const double Can::pi=3.1415926;
测试主函数
#include <iostream>
#include "Box.h"
#include "ToughPack.h"
#include "Carton.h"
#include "Can.h"
using std::cout;
using std::endl;
int main(){
//初始化容器
Box aBox(40,30,20);
Can aCan(10,3);
Carton aCarton(40,30,20);
ToughPack hardcase(40,30,20);
Vessel* pVessels[]={&aBox,&aCan,&aCarton,&hardcase};
cout<<endl;
for(int i=0;i<sizeof pVessels/sizeof(pVessels[0]);i++){
cout<<"Volume is"<<pVessels[i]->value<<endl;
}
return 0;
}
通过指针释放对象
示例代码如下:
在vesel类的头文件中添
class Vassel{
public:
virtual double volume() const=0;// 虚函数
~Vessel();//析构函数
}
实现文件中添加如下代码
#include <iostream>
#include "Vessel.h"
Vessel::~Vessel(){
sed::cout<<"Vessel destructor"<<std::endl;
}
在派生类加添加相同的输出一句
测试主函数如下:
#include <iostream>
#include "Box.h"
#include "ToughPack.h"
#include "Carton.h"
#include "Can.h"
using std::cout;
using std::endl;
int main(){
//初始化容器
Box aBox(40,30,20);
Can aCan(10,3);
Carton aCarton(40,30,20);
ToughPack hardcase(40,30,20);
Vessel* pVessels[]={&aBox,&aCan,&aCarton,&hardcase};
cout<<endl;
for(int i=0;i<sizeof pVessels/sizeof(pVessels[0]);i++){
cout<<"Volume is"<<pVessels[i]->value<<endl;
}
//释放资源
for(int i=0;i<sizeof pVessels/sizeof(pVessels[0]);i++){
delete pVessels[i];
}
return 0;
}
结果是几个类在释放资源时都调用基类Vessel destructor的析构函数
虚析构函数
在析构函数声明中添加关键字virtual
调用虚拟析构函数,示例代码如下:
class Vessel{
public :
virtual double volume() const=0;//虚函数
virtual ~vessel();//虚析构函数
};
在运行期间标识类型
检查指针指向类型 (typeid()运算符,需在头文件中包含<typeinfo>
)
if(typeid(* pVessel)==typeid(Carton)){
cout<"Pointer is type Carton"<<endl;
}else{
cout<"Pointer is not type Carton"<<endl;
}
类成员的指针
数据成员指针 它包含类成员的地址,仅在与类对象组合使用时,才指向内
存的某个位置
示例代码如下:
class Box{
public :
Box( Box(double lenthValue=1.0,double
widthValue=1.0,double heightValue=1.0);
void showVolume() const;
public : //原始状态为protected
double length;
double width;
double height;
}
//定义一个指针
double Box::* pData;
//注创建同意词的语名
typedef double Box:: * pBoxMember;//pBoxMember替代double Box::*
//下面的语句把类成员width的地址赋于这个指针
pData=&Box::width;
"成员指针"选择运算符
成员指针总是要和对象、对象的引用或对象的指针一起使用
示例代码如下:
Box myBox(20.0,30.0,40.0);
调用myBox的width成员
myBox.*pData
应用Box指针
Box* pBox=&myBox;
通过指针访问myBox 中的数据成员
pBox->*pData//此处用到间接成员指针->*
使用成员指针,示例代码
#include <iostream>
#include "Box.h"
#include "ToughPack.h"
#include "Carton.h"
using std::cout;
using std::endl;
typedef double Box::* pBoxMember;
int main(){
//初始化三个容器
Box myBox(20.0,30.0,40.0);
ToughPack hardcase(35.0,45.0,55.0);
Carton aCarton(48.0,58.0,68.0);
//使用成员指针,指向成员长度
pBoxMember pData=&Box::length;
cout<<endl;
cout<<"Length member of myBox is"<<myBox.*pData<<endl;
pData=&Box::width;//指向成员宽度
cout<<"width member of myBox is"<<myBox.*pData<<endl;
pData=&Box::height;//指向成员高度
cout<<"height member of myBox is"<<myBox.*pData<<endl;
cout<<"height member of hardcase is"<<hardcase.*pData<<endl;
cout<<"height member of aCarton is"<<aCarton.*pData<<endl;
//利指针访问成员指针中的成员
Box* pBox=&myBox;
cout<<"height memeber myBox is"<<pBox->*pData<<endl;
pBox=&hardcase;
cout<<"height memeber hardcase is"<<pBox->*pData<<endl;
cout<<endl;
return 0;
}