C++之多态和虚函数

来源:互联网 发布:逆战网络异常 编辑:程序博客网 时间:2024/05/29 14:28

一.什么是多态

        多态与封装,继承一起构成了面向对象的3大特性。多态指向不同对象发送同一消息,不同的对象会产生不同的行为。也就是说每个对象用自己的方式去响应共同的消息。C++有俩中形式的多态。如上课铃打了,不同班级的同学走向不同的教室。

编译时的多态性,即静态联编:

程序在编译之前就可以确定的多态性,通过重载机制来实现的,可以是函数重载,也可以是运算符重载。


运行时多态性,即动态联编:

     必须在运行时才可以确定的多态性,通过继承和虚函数来实现。使用virtual关键字修饰类的成员函数时,指明该函数为虚函数,派生类需要重新实现,编译器将实现动态绑定。

  如:virtual double area() { return 0;  }

 

 

 

 

 

二..虚函数的定义

     为了实现某种功能而假设的函数称为虚函数。

const double IP = 3.14159;

class Cpint

{

public:

Cpint(double x,double y)

{

  this->x = x;

  this->y = y;

}

virtual double area()

{

  return 0;

 

}

private:

double x,y;

};

 

 

 

class Ccircle : public Cpint

{

public:

Ccircle(double x,double y,double radius) : Cpint(x,y)

{

 this->radius = radius;

}

double area()

{

 return IP * radius * radius;

}

private:

double radius;

};

void main()

{

  Cpint pint(4.0,4.0);

  Ccircle circle(5.0,6.0,10);

  cout <<"area of Cpint is "<<pint.area()<<endl;

  cout<<"area of Ccircle is "<<circle.area()<<endl;

 

  Cpint *ptr_pint = &pint;

  cout<<"area of ptr_pint is "<<ptr_pint->area()<<endl;

 

  Ccircle *ptr_circle = &circle;

  cout<<"area of ptr_circle is "<<ptr_circle->area()<<endl;

 

  ptr_pint = &circle;

  cout<<"area of Ccircle is "<<ptr_pint->area()<<endl;

  system("pause");

}


但是如果要让程序实现下面这种结果:


     就是要Cpint的指针指向ptr_Ccircle的成员函数area的地址,为了给俩个函数一个新的标识符,使他们区分开来,引入关键字:virtual,

将这种函数称为虚函数:

Virtual double area() {return 0;}

如果派生类没有改写继承类的虚函数,则函数指针调用基类的虚函数。如果派生类改写了基类的虚函数,编译器将重新为派生类的虚函数建立地址,函数指针调用改写过的虚函数。

总结:如果派生类中有与基类同名的成员函数,并且基类指针指向派生类的对象,那么基类指针调用的函数是派生类的函数




三..动态联编的工作机制。

     当编译器编译遇到virtual关键字的时候,将自动为包含virtual的函数建立一张虚拟函数表,在这个虚函数表中依次存放了特定虚函数的地址。同时在每个带有虚函数的类中放置一个指针。如果基类的成员函数定义为虚函数,那么他所在的派生类中也保持为虚函数,(virtual在派生类中可以省略)

对于虚函数有以下几点说明:

1.基类成员函数定义为虚函数后,要使达到动态联编的效果,派生类和基类对应的函数名,返回值类型,参数个数和类型也必须相同。

2.基类中虚函数的virtual关键字不能省略,派生类中虚函数的关键字可以省略。

3.运行时虚函数必须通过基类对象的引用或基类对象的指针调用虚函数才能实现。

4.虚函数必须是类的成员函数,不能是友元函数也不能是静态成员函数。

5.不能将构造函数定义成虚函数(构造函数执行时,对象还没有完全构造好),但可以将析构函数定义成虚函数。

 

 

 

四.虚析构函数

如果基类析构函数为虚析构函数,则释放基类指针的时候会调用基类和派生类中所有的析构函数,派生类对象的所有内存空间都将被释放。因此c++中析构函数通常为虚析构函数。


定义方法为:

Vietual  ~ 类名();

 

 

 

 

五.纯虚函数与抽象类

          纯虚函数是在声明虚函数时被“初始化”为0的函数,纯虚函数只有函数的名字而不具备函数的功能,不能被调用。至少含有一个纯虚函数的类称为抽象类

         纯虚函数:它的作用是在基类中为其派生类保留一个函数的名字,以便派生类根据需要对它进行定义。如果在基类中,没有保留函数的名字,无法实现多态。纯虚函数的一般形式为:

Class 类名

{

  Virtual 类型 函数名(参数表)= 0//纯虚函数

  .......

};

注意:1.纯虚函数没有函数体

      2.=0”只是形式上的作用,告诉编译器此为纯虚函数。

 

 

抽象类:class 类名

       {

        Public :

              Virtual 返回值类型 函数名(参数表)= 0

               其他函数的而声明;

       }

注意:1.抽象类只能用作其他类的基类,抽象类不能建立对象

      2.抽象类不能用作函数参数类型、函数返回类型或显示转换类型。

      3.可以声明抽象类的指针和引用。

 

抽象类实例:

class CPerson   //抽象类型

{

public:

virtual void PrintInfo()  //基类中的虚构函数

{

  cout <<"人类\n";

}

virtual void DisplaySalary(int m,double s) = 0;//抽象类中的纯虚函数

};

class CWorker :public CPerson

{

public:

   void PrintInfo()   //在派生类中重新定义

   {

     cout <<"工人\n";

    }

   void DisplaySalary(int m,double s)

   {

     cout <<"工人全年的工资是:"<<m*s<<endl;

    }

private:

int kindofwork;

};

class CTeacher:public CPerson

{

public:

void PrintInfo()

{

 cout << "教师\n";

}

void DisplaySalary(int m,double s)

{

    cout <<"教师全年的工资:"<<m*s << endl;

}

private:

int subject;

};

class CDriver:public CPerson

{

public:

void PrintInfo()

{

 cout <<"司机\n";

}

void DisplaySalary(int m,double s)

{

    cout <<"司机的全年工资是:"<<m*s<<endl;

}

private:

int subject;

};

void main()

{

 CWorker worker;   //分别对三个类的对象进行声明

 CTeacher teacher;

 CDriver driver;

 CPerson *person;   //指向父类的指针变量person

 person = &worker;   //指向派生类,获取的是指向派生类的虚表指针

 person -> PrintInfo();

 person -> DisplaySalary(12,1800.35);

 person = &teacher;

 person -> PrintInfo();

 person -> DisplaySalary(12,1300.45);

 person = &driver;

 person -> PrintInfo();

 person -> DisplaySalary(12,1700.78);

 system("pause");

}

运行结果为:

 

    在这个程序中CPerson类中的虚函数DisplaySalary(int m,double s)仅起到为派生类提供一个接口的作用,而派生类中重新定义的DisplaySalary(int m, double s)用于取决什么样的方式来计算工资。由于在CPerson中不能对此作出决定,因此被说明为纯虚函数。工人,教师和司机等都视为人类的对象,多态性保证了函数在对不同人群计算工资时,无需关心当前正在计算那类人。在需要时函数可以从这些不同人群的子类中获得该对象的工资。




0 0