C++的多态性和虚函数

C++ 多态

多态性是指一个名字,多种语义;或界面相同,多种实现。

静态多态性:通过函数重载或者运算符重载实现,在编译阶段即知道函数的全部调用关系,也称为是编译时的多态性。根据表达式上下文确定该执行哪一个功能。

优点:调用速度快,效率高;缺点:缺乏灵活性。

动态多态性:在运行时实现的多态,即在运行的时候才知道操作所针对的对象。通过虚函数来实现

重载函数是多态性的一种简单形式。

虚函数允许函数调用与函数体的联系在运行时才进行,称为动态联编。

冠以关键字virtual 的成员函数称为虚函数

实现运行时多态的关键首先是要说明虚函数,另外,必须用

基类指针调用派生类的不同实现版本

当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。

C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。

例如基类 Shape 被派生为两个类,如下(该代码摘自知乎):

#include<iostream>

using namespace std;

class Shape {

   protected:

      int width, height;

   public:

      Shape( int a=0, int b=0)

      {

         width = a;

         height = b;

      }

      int area()

      {

         cout << "Parent class area:" <<endl;

         return 0;

      }

};

class Rectangle:public Shape{

   public:

      Rectangle( int a=0, int b=0):Shape(a, b){ }

      int area ()

      {

         cout << "Rectangle classarea :" <<endl;

         return (width * height);

      }

};

class Triangle:public Shape{

   public:

      Triangle( int a=0, int b=0):Shape(a, b) {}

      int area ()

      {

         cout << "Triangle classarea :" <<endl;

         return (width * height / 2);

      }

};

// 程序的主函数

int main( )

{

   Shape *shape;

   Rectangle rec(10,7);

   Triangle tri(10,5);

   // 存储矩形的地址

   shape = &rec;

   // 调用矩形的求面积函数 area

   shape->area();

   // 存储三角形的地址

   shape = &tri;

   // 调用三角形的求面积函数 area

   shape->area();

   return 0;

}

 
[/demo]


当上面的代码被编译和执行时,它会产生下列结果:

Parent class area

Parent class area

导致错误输出的原因是,调用函数 area() 被编译器设置为基类中的版本,这就是所谓的静态多态,或静态链接 - 函数调用在程序执行前就准备好了。有时候这也被称为早绑定,因为 area() 函数在程序编译期间就已经设置好了。

但现在,让我们对程序稍作修改,在 Shape 类中,area() 的声明前放置关键字 virtual,如下所示:

class Shape {

   protected:

      int width, height;

   public:

      Shape( int a=0, int b=0)

      {

         width = a;

         height = b;

      }

      virtualint area()

      {

         cout << "Parent class area:" <<endl;

         return 0;

      }

};

修改后,当编译和执行前面的实例代码时,它会产生以下结果:

Rectangle class area

Triangle class area

此时,编译器看的是指针的内容,而不是它的类型。因此,由于 tri 和 rec 类的对象的地址存储在 *shape 中,所以会调用各自的 area() 函数。

每个子类都有一个函数 area() 的独立实现。这就是多态的一般使用方式。有了多态,就可以有多个不同的类,都带有同一个名称但具有不同实现的函数,函数的参数甚至可以是相同的。

虚函数

虚函数 是在基类中使用关键字 virtual 声明的函数。

重载特性:

在派生类中重载基类的虚函数要求函数名、返回类型、参数个数、参数类型和顺序完全相同

如果仅仅返回类型不同,C++认为是错误重载

如果函数原型不同,仅函数名相同,丢失虚特性

虚析构函数

构造函数不能是虚函数。建立一个派生类对象时,必须从类层次的根开始,沿着继承路径逐个调用基类的构造函数

析构函数可以是虚的。虚析构函数用于指引 delete 运算符正确析构动态对象

[/demo]

 

#include<iostream>

using namespace std ;

class A

 {public:

       ~A(){ cout << "A::~A() is called.\n" ; }

 } ;

class B : public A

 {public:

       ~B(){ cout << "B::~B() is called.\n" ; }

} ;

int main()

 { A*Ap = new B ;  

    B*Bp2 = new B ;

   cout << "delete first object:\n" ;

   delete Ap;

   cout << "delete second object:\n" ;

   delete Bp2 ;

}

 

[/demo]

 

当上面的代码被编译和执行时,它会产生下列结果:

 

delete first object:

B::~B() is called.

A::~A() is called.

delete second object:

B::~B() is called.

A::~A() is called.

纯虚函数

如果要在基类中定义虚函数,以便在派生类中重新定义该函数更好地适用于对象,但是在基类中又不能对虚函数给出有意义的实现,这个时候就会用到纯虚函数。


class  point { /*……*/ } ;

class  shape ;       // 抽象类

{ point  center ;

      ……

  public :

  point where ( ) { return  center ; }

  void move ( point p ) {center = p ; draw ( ) ; }

  virtual void  rotate ( int ) = 0 ;      // 纯虚函数

  virtual void  draw ( ) = 0 ;        // 纯虚函数

} ;

      …...

基于以上的代码:

Shape  x ;        // error,抽象类不能建立对象

shape  *p ;       //ok,可以声明抽象类的指针

shape  f ( ) ;    // error, 抽象类不能作为函数返回类型

void  g ( shape ) ;      //error, 抽象类不能作为传值参数类型

shape  & h ( shape &) ;  // ok,可以声明抽象类的引用

 

多态性和虚函数的学习心得:

多态与非多态的区别在于成员函数调用地址的早绑定和晚绑定早绑定在编译期就可以确定函数的调用地址,是静态的;晚绑定在运行时才能确定函数的调用地址,是动态的。
  在面向对象的编程中,封装使得代码模块化;继承可以扩展以存在的代码;多态使得接口重用。虚函数是多态性的c++实现。

猜你喜欢

转载自blog.csdn.net/weixin_40630836/article/details/80881483