多态是c++的三大特征之一,多态是指同样的消息被不同类型的对象接收时导致了不同的行为,本质上是对类的成员函数的操作,即调用了不同的函数。多态有哪些类型呢?面向对象的多态性可以分为四类:重载多态(函数重载)、强制多态(强制类型转化)、包含多态(主要通过虚函数来实现,研究类族中定义与不同类中同名函数的多态行为)、参数多态。
重载多态与强制多态属于专用多态,包含多态与参数多态属于通用多态。重载多态多表现为对普通函数或者类的成员函数的重载和运算符重载。运算符重载是对已有的运算符赋予多重定义,使相同的运算符作用于不同类型的数据时才生不同的行为。运算符重载在实现的过程中把指定的运算符转化为对运算符函数的调用,运算的对象转化为运算符函数的实参,编译器通过实参的类型来调用不同的函数,此过程在编译时完成的,属于静态绑定。不能被重载的运算符有5个:类属关系运算符“.”、成员指针运算符‘’.*”、作用域分辨率“::”、sizeof运算符、三目运算符。运算符重载形式有2种:重载为类的成员函数和类的友元函数,运算符重载的关键字为operator。
1)重载为类的成员函数:
函数类型 operator 运算符(形参表)
{
函数体;
定义了一个基类Box,重载+运算符函数为Box的成员函数;
Box & operator ++()//前置单目运算符
{
radius++;
height++;
return *this;
}
// ++(*this);
Box old = *this;
--(*this);
return old;
由于是成员函数,在某个对象调用这个函数时,自身的数据可以直接访问,就不需要再放在参数表中传递,因此比重载为友元函数的参数个数少一个。
调用时:Box a,b; Box c=a+b;
2)重载为类的友元函数
友元函数可以自由地访问该类的任何数据成员,运算所需要的操作数通过函数的形参传递
friend 函数类型 operator 运算符(形参表)
{
函数体;
}
friend Box operator + (Box c, Box d)
{
return Box(c.radius+d.radius,c.height+d.height);
}
调用时:Box a,b; Box c=a+b;
前置运算符++ -- 与后置运算符++ --的重载函数的区别是后置运算符重载函数的参数多一个int 类型参数,这个参数在运算中不起任何作用。
对比如下:
Box & operator ++()//前置单目运算符
{
radius++;
height++;
return *this;
}
Box operator ++(int)//后置单目运算符重载
{
Box old = *this;
++(*this);又调用了一次前置单目重载函数
return old;
}
Box operator + (Box c)
{
return Box(radius+c.radius,height+c.height);//返回一个匿名对象
}
函数体中的return Box();并不是对构造函数的调用,而是“创建一个临时对象并返回它”,也可以以如下的形式返回:
Box operator + (Box c)
{
Box d(radius + c.radius, height + c.height);
return d;
}
这两种方法的执行效率是完全不同的。后者的执行过程是这样的:创建一个局部对象c(这时会调用构造函数),执行return语句时会调用拷贝构造函数,将c的值拷贝到主调函数中的一个无名临时对象中。当函数operator+结束时,会调用析构函数析构对象c,然后,c消亡;前一种方法的效率明显高多了,是直接将一个无名临时对象创建到主调函数中。
在一个人员管理系统中,employee 是应该基类,manager,technician,salesman等都public继续这个基类,
如下:
class employee
{
protected:
char name[20];
int individualEmpNO;
int grade;
float accumPay;
static int employeeNo;
public:
employee();
~employee();
virtual viod promote(int increment=0);
......};
class manager:public employee
{
......
public:
void pay(){accumPay=....}
}
class technician:public employee
{
......
public:
void pay(){accumPay=....}
}
class salesman:public employee
{
......
public:
void pay(){accumPay=....}
}
虚函数的语法声明为:
virtual 函数类型 函数名(形参表)
{
函数体;
}
在基类employee中,虚函数为 virtual void pay(){}
派生类中可以不显式声明虚函数,系统会根据该函数与基类的虚函数是否有相同的名称、参数个数、参数类型、返回值或者满足类型兼容规则的指针、引用型的返回值来判断是不是虚函数。
当基类的构造函数调用了虚函数时,不会调用派生类的虚函数,析构时同样。
如果:
void fun(Base *b)
{
delete b;
}
int main()
{
Base *b= new Derived();
fun(b);
}
当如上通过基类指针删除派生类对象时,系统调用的是基类的析构函数,而没有执行派生类的析构函数,倘若在派生类中有人工开辟空间的话,这时因为没有释放会造成内存泄露。
那么这么解决呢?多态!!!就是让基类的析构函数成为虚析构函数: virtual ~Base(0{}
上面提到的人员管理程序中,基类的pay()函数体为空,显得冗余。 对于这种在基类中无法实现的函数,能否在基类中只说明函数原型用来规定整个类族的统一接口形式,而在派生类中再给出函数的具体实现呢? 在c++中提供了纯虚函数来说实现这一功能。纯虚函数是一个在基类中声明的虚函数,它在基类中没有定义具体的操作内容,要求各派生类根据实际需要定义自己的版本,纯虚函数的声明格式如下:
virtual 函数类型 函数名(参数表)=0;
带有纯虚函数的类是抽象类,抽象类不能实例化,就是不能定义一个抽象类的对象,但是可以申明一个抽象类的指针或者引用,如果抽象类派生出新类,派生类给出了所有纯虚函数的实现,则这个派生类就可以定义自己的对象,不再是抽象类。
生活那么美~~~早点睡!!!