c++之多态性(动态绑定)

多态性 
指相同对象收到不同消息或不同对象收到相同消息时产生不同的实现动作。C++支持两种多态性:编译时多态性,运行时多态性。 
  a、编译时多态性:通过重载函数实现 
  b、运行时多态性:通过虚函数实现
 

多态性可以简单地概括为“一个接口,多种方法”,程序在运行时才决定调用的函数,它是面向对象编程领域的核心概念。多态(polymorphism),字面意思多种形状。多态亦可以解释为c++中的动态绑定.

C++多态是怎么实现的?

C++多态性是通过虚函数来实现的,虚函数允许子类重新定义成员函数,而子类重新定义父类的做法称为覆盖(override),或者称为重写。(这里我觉得要补充,重写的话可以有两种,直接重写成员函数和重写虚函数,只有重写了虚函数的才能算作是体现了C++多态性)而重载则是允许有多个同名的函数,而这些函数的参数列表不同,允许参数个数不同,参数类型不同,或者两者都不同。编译器会根据这些函数的不同列表,将同名的函数的名称做修饰,从而生成一些不同名称的预处理函数,来实现同名函数调用时的重载问题。但这并没有体现多态性。

这里我们拿C++primer中的例子来说明:

分别定义Quote类(按原价出售的书籍)和派生类Bulk_quote(按打折价格出售的书籍)

class Quote {
public:
    virtual double net_price(std::size_t n) const;
};

class Bulk_quote : public Quote {
public:
    double net_price(std::size_t) const override;
};

这里Quote定义了虚函数net_price,派生类对其进行了override.因为Bulk_quote是以public方式继承Quote类,public继承是一种"is a"的方式,即Bulk_quote是一种Quote类,可以把Bulk_quote的对象当作Quote的对象来使用.

继续看下面的代码,用于计算所买书籍总价格

double print_total(const Quote &item,size_t n)
{
    double ret = item.net_price(n);
    return ret;
}
因为函数printe_total中的item行参是基类的一个引用,由于上述的"is a"关系,所以我们既可以使用基类Quote的对象来调用它,也可以用派生类Bulk_quote来调用它.那么问题来了? 当我们用Bulk_quote对象调用该函数时,究竟执行基类的net_price还是Bulk_quote中的net_price?

这里就是C++动态绑定也是多态起作用的时候了,根据实际传入的对象类型来决定执行net_price的哪个版本.

//basic的类型是Quote;Bulk的类型是Bulk_quote
print_total(basic, 20); //调用Quote的net_price
print_total(bulk, 20); //调用Bulk_quote的net_price版本

多态与非多态的实质区别就是函数地址是早绑定还是晚绑定。如果函数的调用,在编译器编译期间就可以确定函数的调用地址,并生产代码,是静态的,就是说地址是早绑定的。而如果函数调用的地址不能在编译器期间确定,需要在运行时才确定,这就属于晚绑定。
  那么多态的作用是什么呢,封装可以使得代码模块化,继承可以扩展已存在的代码,他们的目的都是为了代码重用。而多态的目的则是为了接口重用。也就是说,不论传递过来的究竟是那个类的对象,函数都能够通过同一个接口调用到适应各自对象的实现方法。

最常见的用法就是声明基类的指针,利用该指针指向任意一个子类对象,调用相应的虚函数,可以根据指向的子类的不同而实现不同的方法。如果没有使用虚函数的话,即没有利用C++多态性,则利用基类指针调用相应的函数的时候,将总被限制在基类函数本身,而无法调用到子类中被重写过的函数因为没有多态性,函数调用的地址将是一定的,而固定的地址将始终调用到同一个函数,这就无法实现一个接口,多种方法的目的了。


C++纯虚函数

一、定义
  纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加“=0” 
  virtual void funtion()=0 
二、引入原因
   1、为了方便使用多态特性,我们常常需要在基类中定义虚拟函数。 
   2、在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。 

又例如:

class Shape {
public:
    virtual void draw() const = 0;
    ....
};

class Rectangle : public Shape {...};  //矩形
class Ellipse : public Shape {...};  //椭圆
这里Shape类只为Rectangle和Ellipse提供draw接口. 所有的Shape对象都应该是可以绘出的,但是Shape基类无法为此函数提供合理的缺省实现,毕竟椭圆绘法和矩形绘法不一样!Shape::draw的声明式是对派生类设计人员说:"你必须提供一个draw函数,但是我不管你怎么实现它".定义纯虚函数同时 起到一个规范的作用,规范继承这个类的程序员必须实现这个函数
为了解决上述问题,引入了纯虚函数的概念,将函数定义为纯虚函数(方法:virtual ReturnType Function()= 0;),则编译器要求在派生类中必须予以重写以实现多态性。同时含有纯虚拟函数的类称为抽象类,它不能生成对象。这样就很好地解决了上述两个问题

参考资料:

http://blog.csdn.net/hackbuteer1/article/details/7475622

<<Effective c++>>


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值