多态的基本概念
实现了多态机制的程序,可以使用同一个名字完成不同的功能。
多态分为编译时多态和运行时多态。
多态
静态多态在编译期间就可以确定函数的调用地址,并产生代码。
函数调用与代码入口地址的绑定需要在运行时刻才能确定,称为动态联编或动态绑定。
在类之间满足赋值兼容的前提下,实现动态绑定必须满足以下两个条件
- 必须声明虚函数
- 通过基类类型的引用或者指针调用虚函数。
虚函数
所谓虚函数,就是在函数声明时前面加了virtual关键字的成员函数。
声明虚函数成员的格式
virtual 函数返回值类型 函数名(形参表);
通过基类指针实现多态
程序6-1 通过基类指针实现多态示例
#include <iostream>
using namespace std;
class A
{
public:
virtual void Print()
{
cout<<"A::Print"<<endl;
}
};
class B:public A
{
public:
virtual void Print()
{
cout<<"B::Print"<<endl;
}
};
class D:public A
{
public:
virtual void Print()
{
cout<<"D::Print"<<endl;
}
};
class E:public B
{
public:
virtual void Print()
{
cout<<"E::Print"<<endl;
}
};
int main()
{
A a; B b; D d; E e;
A *pa = &a; //基类指针pa指向基类对象a
B *pb = &b; //派生类指针pb指向派生类对象B
pa->Print(); //多态,目前指向基类对象,调用a.Print(),输出A::Print
pa = pb; //派生类指针赋给基类指针,pa指向派生类对象b
pa->Print(); //多态,目前指向派生类对象,调用b.Print(),输出B::Print
pa = &d; //基类指针pa指向派生类对象d
pa->Print(); //多态,目前指向派生类对象,调用d.Print(),输出D::Print
pa = &e; //基类指针pa指向派生类对象e
pa->Print(); //多态,目前指向派生类对象,调用e.Print(),输出E::Print
return 0;
}
A::Print
B::Print
D::Print
E::Print
程序6-2 用基类指针访问基类对象及派生类对象
#include <iostream>
#include <string>
using namespace std;
class A
{
public:
void put_name(string s)
{
name = s;
}
virtual void print_name() const
{
cout<<"A::"<<name<<"\n";
}
string name;
};
class B:public A
{
public:
void put_name(string s)
{
put_name(s);
}
virtual void print_name() const
{
cout<<"B::"<<name<<","<<A::name<<"\n";
}
void put_phone(string num)
{
phone_num = num;
}
void print_phone() const
{
cout<<phone_num<<"\n";
}
string phone_num; //派生类中的成员变量
};
int main()
{
A *A_p;
A A_obj;
B B_obj;
A_p = &A_obj; //基类指针指向基类对象
A_p->put_name("多态示例_名字"); //赋给基类对象A_obj
cout<<"A_p->print_name的输出内容:\t";
A_p->print_name(); //使用指针输出,A_obj中的值
cout<<"A_obj.print_name()的输出内容:\t";
A_obj.print_name(); //使用对象输出
A_p = &B_obj; //基类指针指向派生类对象
A_p->put_name("另一个名字"); //多态,赋给派生类对象B_obj
cout<<"A_p->print_name()的输出内容:\t";
A_p->print_name(); //多态,使用指针输出,B_obj中的值,继承的name的值
cout<<"B_obj.print_name()的输出内容:\t";
B_obj.print_name(); //使用对象输出,B_obj中的值,继承的name值
B_obj.put_phone("电话号码999"); //派生类对象赋值
cout<<"((B *)A_p)->print_phone()的输出内容:\t";
((B*)A_p)->print_phone(); //强制转换基类指针,输出的是派生类对象中的值
cout<<"B_obj.print_phone()的输出内容:\t\t";
B_obj.print_phone(); //输出的是派生类对象中的值
return 0;
}
A_p->print_name的输出内容: A::多态示例_名字
A_obj.print_name()的输出内容: A::多态示例_名字
A_p->print_name()的输出内容: B::另一个名字,另一个名字
B_obj.print_name()的输出内容: B::另一个名字,另一个名字
((B *)A_p)->print_phone()的输出内容: 电话号码999
B_obj.print_phone()的输出内容: 电话号码999
通过基类引用实现多态
通过基类指针调用虚函数时可以实现多态,通过基类的引用调用虚函数的语句也是多态的。
程序6-3 基类引用实现多态
#include <iostream>
using namespace std;
class A
{
public:
virtual void Print()
{
cout<<"A::Print"<<endl;
}
};
class B:public A
{
public:
virtual void Print()
{
cout<<"B::Print"<<endl;
}
};
void PrintInfo(A &r)
{
r.Print(); //多态,使用基类引用调用哪个Print()取决于r引用了哪个类的对象
}
int main()
{
A a;
B b;
PrintInfo(a);
PrintInfo(b);
return 0;
}
A::Print
B::Print
*多态的实现原理
多态的关键在于通过基类指针或引用调用一个虚函数时,编译阶段不能确定到底调用的时基类还是派生类的函数,运行时才能确定。
程序6-4 多态机制下对象存储空间的大小
#include <iostream>
using namespace std;
class A
{
public:
int i;
virtual void func(){}
virtual void func2(){}
};
class B:public A
{
int j;
void func(){}
};
int main()
{
cout<<sizeof(A)<<","<<sizeof(B);
return 0;
}
16,16
多态实例
在面向对象的程序设计中,使用多态能够增强程序的可扩充性。
使用多态也能起到精简代码的作用。
程序6-5 使用多态出来图形示例
#include <iostream>
#include <cmath>
using namespace std;
class CShape //基类:图形类
{
protected:
double acreage; //图形的面积,子类可以访问
public:
CShape()
{
// cout<<"基类构造函数"<<endl;
};
virtual ~CShape()
{
// cout<<"基类析构函数"<<endl;
};
virtual double CalAcr() //计算面积,虚函数
{
return 0;
};
virtual void setAcreage(double acre){}; //设置面积值,虚函数
virtual void PrintInfo(){}; //显示信息,虚函数
};
class CRectangle:public CShape //派生类:矩形类
{
double width,high; //矩形的宽度和高度
public:
CRectangle(double w,double h)
{
// cout<<"矩形带参构造函数"<<endl;
width = w;
high = h;
};
CRectangle()
{
// cout<<"矩形无参构造函数"<<endl;
width = 0;
high = 0;
};
~CRectangle()
{
// cout<<"矩形析构函数"<<endl;
};
virtual double CalAcr(); //继承的函数,虚函数
virtual void PrintInfo(); //继承的函数,虚函数
virtual void setAcreage(double); //继承的函数,虚函数
};
class CCircle:public CShape //派生类:圆形类
{
double radius; //半径,私有的
public:
CCircle(double r)
{
// cout<<"圆形带参构造函数"<<endl;
radius = r;
};
CCircle()
{
// cout<<"圆形无参构造函数"<<endl;
radius = 0;
};
~CCircle()
{
// cout<<"圆形析构函数"<<endl;
};
virtual double CalAcr(); //继承的函数,虚函数
virtual void PrintInfo(); //继承的函数,虚函数
virtual void setAcreage(double); //继承的函数,虚函数
};
class CTriangle:public CShape //派生类:三角形类
{
double a,b,c; //三条边的长度,私有的
public:
CTriangle(double a,double b,double c)
{
// cout<<"三角形带参构造函数"<<endl;
this->a = a;
this->b = b;
this->c = c;
};
CTriangle()
{
// cout<<"三角形无参构造函数"<<endl;
a = 0;
b = 0;
c = 0;
};
~CTriangle()
{
// cout<<"三角形析构函数"<<endl;
};
virtual double CalAcr(); //继承的函数,虚函数
virtual void PrintInfo(); //继承的函数,虚函数
virtual void setAcreage(double); //继承的函数,虚函数
};
double CRectangle::CalAcr() //计算矩形面积
{
return width * high;
}
void CRectangle::PrintInfo() //输出矩形信息
{
cout<<"矩形。\t宽度="<<this->width<<",高度="<<this->high<<",面积="<<this->acreage<<endl;
}
void CRectangle::setAcreage(double acre) //设置面积值
{
acreage = acre; //访问基类保护成员
}
double CCircle::CalAcr() //计算圆形的面积
{
return 3.14*radius*radius;
}
void CCircle::PrintInfo() //输出圆形信息
{
cout<<"圆。\t半径="<<this->radius<<",面积="<<this->acreage<<endl;
}
void CCircle::setAcreage(double acre)
{
acreage = acre;
}
double CTriangle::CalAcr() //根据海伦公式计算三角形面积
{
double p = (a+b+c)/2.0;
return sqrt(p*(p-a)*(p-b)*(p-c));
}
void CTriangle::PrintInfo()
{
cout<<"三角形。三条边分别是:"<<this->a<<","<<this->b<<","<<this->c<<",面积="<<this->acreage<<endl;
}
void CTriangle::setAcreage(double acre)
{
acreage = acre;
}
CShape *pShapes[100]; //用来存储各种几何形状,最对100个
int main()
{
int i,n;
double temp1,temp2,temp3;
CRectangle *pr;
CCircle *pc;
CTriangle *pt;
cin>>n;
for(i=0;i<n;++i)
{
char c;
cin>>c;
switch(c)
{
case 'R':case 'r': //矩形
cin>>temp1>>temp2;
pr = new CRectangle(temp1,temp2);
pr->setAcreage(pr->CalAcr());
pShapes[i] = pr;
break;
case 'C':case 'c':
cin>>temp1; //圆形
pc = new CCircle(temp1);
pc->setAcreage(pc->CalAcr());
pShapes[i] = pc;
break;
case 'T':case 't': //三角形
cin>>temp1>>temp2>>temp3;
pt = new CTriangle(temp1,temp2,temp3);
pt = new CTriangle(temp1,temp2,temp3);
pt->setAcreage(pt->CalAcr());
pShapes[i]= pt;
break;
}
}
if(n == 1) cout<<"共有"<<n<<"种图形,它是:"<<endl;
else cout<<"共有"<<n<<"种图形,分别是:"<<endl;
for(i=0;i<n;++i)
{
pShapes[i]->PrintInfo();
delete pShapes[i]; //释放空间
}
return 0;
}
3
C 6
R 7.6 8.2
T 3 4 5
共有3种图形,分别是:
圆。 半径=6,面积=113.04
矩形。 宽度=7.6,高度=8.2,面积=62.32
三角形。三条边分别是:3,4,5,面积=6
多态的使用
类的成员函数直接按可以互相调用。
在普通成员函数(静态成员函数、构造函数和析构函数除外)中调用其他虚成员函数也是允许的,并且是多态的。
程序6-6 在成员函数中调用虚函数
#include <iostream>
using namespace std;
class CBase
{
public:
void func1() //不是虚函数
{
cout<<"CBase::func1()"<<endl;
func2(); //在成员函数中调用虚函数
func3();
}
virtual void func2()
{
cout<<"CBase::func2()"<<endl;
}
void func3()
{
cout<<"CBase::func3()"<<endl;
}
};
class CDerived:public CBase
{
public:
virtual void func2()
{
cout<<"CDerived::func2()"<<endl;
}
void func3()
{
cout<<"CDerived::func3()"<<endl;
}
};
int main()
{
CDerived d;
d.func1();
return 0;
}
CBase::func1()
CDerived::func2()
CBase::func3()
程序6-7 在构造函数与析构函数中调用虚函数
#include <iostream>
using namespace std;
class A
{
public:
virtual void hello()
{
cout<<"A::hello"<<endl;
}
virtual void bye()
{
cout<<"A::bye"<<endl;
}
};
class B:public A
{
public:
virtual void hello()
{
cout<<"B::hello"<<endl;
}
B()
{
hello();
}
~B()
{
bye();
}
};
class C:public B
{
public:
virtual void hello()
{
cout<<"C::hello"<<endl;
}
};
int main()
{
C obj;
return 0;
}
B::hello
A::bye
实现多态时,必须满足的条件是:使用基类指针或引用来调用基类中声明的虚函数。
程序6-8 多态与非多态的对比
#include <iostream>
using namespace std;
class A
{
public:
void func1()
{
cout<<"A::func1"<<endl;
}
virtual void func2()
{
cout<<"A::func2"<<endl;
}
};
class B:public A
{
public:
virtual void func1()
{
cout<<"B::func1"<<endl;
}
void func2()
{
cout<<"B::func2"<<endl;
}
};
class C:public B
{
public:
void func1()
{
cout<<"C::func1"<<endl;
}
void func2()
{
cout<<"C::func2"<<endl;
}
};
int main()
{
C obj;
A *pa = &obj;
B *pb = &obj;
pa->func2(); //多态
pa->func1(); //不是多态
pb->func1(); //多态
return 0;
}
C::func2
A::func1
C::func1
虚析构函数
声明虚析构函数的格式
virtual ~类名();
虚析构函数没有返回值类型,没有参数。
如果一个类的析构函数是虚函数,则由它派生的所有子类的析构函数也是虚析构函数。
程序6-9 不使用虚析构函数的情况
#include <iostream>
using namespace std;
class ABase
{
public:
ABase()
{
cout<<"ABase 构造函数"<<endl;
}
~ABase()
{
cout<<"ABase 析构函数"<<endl;
}
};
class Derived:public ABase
{
public:
int w,h;
Derived()
{
cout<<"Derived 构造函数"<<endl;
w = 4;
h = 7;
}
~Derived()
{
cout<<"Derived 析构函数"<<endl;
}
};
int main()
{
ABase *p = new Derived();
delete p;
return 0;
}
ABase 构造函数
Derived 构造函数
ABase 析构函数
改写,将基类析构函数修改为虚析构函数。
virtual ~ABase()
{
cout<<"ABase 析构函数"<<endl;
}
ABase 构造函数
Derived 构造函数
Derived 析构函数
ABase 析构函数
纯虚函数喝抽象类
纯虚函数
纯虚函数是声明在基类中的虚函数,没有具体的定义,而由各派生类根据识记需要给出各自的定义。
声明纯虚函数的格式
virtual 函数类型 函数名(参数表) = 0;
抽象类
包含纯虚函数的类称为抽象类。
纯虚函数和函数体为空的虚函数的区别:
- 纯虚函数没有函数体,而空的虚函数的函数体为空。
- 纯虚函数所在的类是抽象类,不能直接进行实例化;而空的虚函数所在的类是可以实例化的。
共同特点是:都可以派生出新的类,然后在新类中给出虚函数的实现,而且这种新的实现具有多态特征。
程序6-10 抽象类示例
#include <iostream>
using namespace std;
class A
{
private:
int a;
public:
virtual void print() = 0;
void func1()
{
cout<<"A_func1"<<endl;
}
};
class B:public A
{
public:
void print();
void func1()
{
cout<<"B_func1"<<endl;
}
};
void B::print()
{
cout<<"B_print"<<endl;
}
int main()
{
// A a; //错误,抽象类不能实例化
// A *p = new A; //错误,不能创建类A的实例
// A b[2]; //错误,不能声明抽象类的数组
A *pa; //正确,可以声明抽象类的指针
A *pb = new B;
pb->print();
B b;
A *pc = &b;
pc->func1();
return 0;
}
B_print
A_func1
虚基类
定义虚基类的格式
class 派生类名 : virtual 派生方式 基类名
{
派生类体
};
程序6-11 虚基类
#include <iostream>
using namespace std;
class A
{
public:
int a;
void showa()
{
cout<<"a="<<a<<endl;
}
};
class B:virtual public A
{
public:
int b;
};
class C:virtual public A
{
public:
int c;
};
class D:public B,public C
{
//派生类D的两个基类B、C具有共同的基类A,
//采用了虚继承,从而使类D的对象中只包含着类A的1个实例
public:
int d;
};
int main()
{
D Dobj;
Dobj.a = 11;
Dobj.b = 22;
Dobj.showa();
cout<<"Dobj.b="<<Dobj.b<<endl;
}
a=11
Dobj.b=22
网友评论