`

c++学习笔记十五

    博客分类:
  • c++
 
阅读更多

虚函数和多态性

使用基类指针,示例代码如下(从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;
}

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics