C++ 多态

多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。

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

下面的实例中,基类 Shape 被派生为两个类,如下所示:

实例

  1. #include <iostream>
  2. using namespace std;
  3.  
  4. class Shape {
  5. protected:
  6. int width, height;
  7. public:
  8. Shape( int a=0, int b=0)
  9. {
  10. width = a;
  11. height = b;
  12. }
  13. int area()
  14. {
  15. cout << "Parent class area :" <<endl;
  16. return 0;
  17. }
  18. };
  19. class Rectangle: public Shape{
  20. public:
  21. Rectangle( int a=0, int b=0):Shape(a, b) { }
  22. int area ()
  23. {
  24. cout << "Rectangle class area :" <<endl;
  25. return (width * height);
  26. }
  27. };
  28. class Triangle: public Shape{
  29. public:
  30. Triangle( int a=0, int b=0):Shape(a, b) { }
  31. int area ()
  32. {
  33. cout << "Triangle class area :" <<endl;
  34. return (width * height / 2);
  35. }
  36. };
  37. // 程序的主函数
  38. int main( )
  39. {
  40. Shape *shape;
  41. Rectangle rec(10,7);
  42. Triangle tri(10,5);
  43.  
  44. // 存储矩形的地址
  45. shape = &rec;
  46. // 调用矩形的求面积函数 area
  47. shape->area();
  48.  
  49. // 存储三角形的地址
  50. shape = &tri;
  51. // 调用三角形的求面积函数 area
  52. shape->area();
  53.  
  54. return 0;
  55. }

  

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

  1. Parent class area
  2. Parent class area

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

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

  1. class Shape {
  2. protected:
  3. int width, height;
  4. public:
  5. Shape( int a=0, int b=0)
  6. {
  7. width = a;
  8. height = b;
  9. }
  10. virtual int area()
  11. {
  12. cout << "Parent class area :" <<endl;
  13. return 0;
  14. }
  15. };

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

  1. Rectangle class area
  2. Triangle class area

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

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

虚函数

虚函数 是在基类中使用关键字 virtual 声明的函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。

我们想要的是在程序中任意点可以根据所调用的对象类型来选择调用的函数,这种操作被称为动态链接,或后期绑定

纯虚函数

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

我们可以把基类中的虚函数 area() 改写如下:

  1. class Shape {
  2. protected:
  3. int width, height;
  4. public:
  5. Shape( int a=0, int b=0)
  6. {
  7. width = a;
  8. height = b;
  9. }
  10. // pure virtual function
  11. virtual int area() = 0;
  12. };

= 0 告诉编译器,函数没有主体,上面的虚函数是纯虚函数

C# 多态性

多态性意味着有多重形式。在面向对象编程范式中,多态性往往表现为"一个接口,多个功能"。

多态性可以是静态的或动态的。在静态多态性中,函数的响应是在编译时发生的。在动态多态性中,函数的响应是在运行时发生的。

静态多态性

在编译时,函数和对象的连接机制被称为早期绑定,也被称为静态绑定。C# 提供了两种技术来实现静态多态性。分别为:

  • 函数重载
  • 运算符重载

运算符重载将在下一章节讨论,接下来我们将讨论函数重载。


函数重载

您可以在同一个范围内对相同的函数名有多个定义。函数的定义必须彼此不同,可以是参数列表中的参数类型不同,也可以是参数个数不同。不能重载只有返回类型不同的函数声明。

下面的实例演示了几个相同的函数 print(),用于打印不同的数据类型:

  1. using System;
  2. namespace PolymorphismApplication
  3. {
  4. class Printdata
  5. {
  6. void print(int i)
  7. {
  8. Console.WriteLine("Printing int: {0}", i );
  9. }
  10.  
  11. void print(double f)
  12. {
  13. Console.WriteLine("Printing float: {0}" , f);
  14. }
  15.  
  16. void print(string s)
  17. {
  18. Console.WriteLine("Printing string: {0}", s);
  19. }
  20. static void Main(string[] args)
  21. {
  22. Printdata p = new Printdata();
  23. // 调用 print 来打印整数
  24. p.print(5);
  25. // 调用 print 来打印浮点数
  26. p.print(500.263);
  27. // 调用 print 来打印字符串
  28. p.print("Hello C++");
  29. Console.ReadKey();
  30. }
  31. }
  32. }

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

  1. Printing int: 5
  2. Printing float: 500.263
  3. Printing string: Hello C++

动态多态性

C# 允许您使用关键字 abstract 创建抽象类,用于提供接口的部分类的实现。当一个派生类继承自该抽象类时,实现即完成。抽象类包含抽象方法,抽象方法可被派生类实现。派生类具有更专业的功能。

请注意,下面是有关抽象类的一些规则:

  • 您不能创建一个抽象类的实例。
  • 您不能在一个抽象类外部声明一个抽象方法。
  • 通过在类定义前面放置关键字 sealed,可以将类声明为密封类。当一个类被声明为 sealed 时,它不能被继承。抽象类不能被声明为 sealed。

下面的程序演示了一个抽象类:

  1. using System;
  2. namespace PolymorphismApplication
  3. {
  4. abstract class Shape
  5. {
  6. public abstract int area();
  7. }
  8. class Rectangle: Shape
  9. {
  10. private int length;
  11. private int width;
  12. public Rectangle( int a=0, int b=0)
  13. {
  14. length = a;
  15. width = b;
  16. }
  17. public override int area ()
  18. {
  19. Console.WriteLine("Rectangle 类的面积:");
  20. return (width * length);
  21. }
  22. }
  23.  
  24. class RectangleTester
  25. {
  26. static void Main(string[] args)
  27. {
  28. Rectangle r = new Rectangle(10, 7);
  29. double a = r.area();
  30. Console.WriteLine("面积: {0}",a);
  31. Console.ReadKey();
  32. }
  33. }
  34. }

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

  1. Rectangle 类的面积:
  2. 面积: 70

当有一个定义在类中的函数需要在继承类中实现时,可以使用虚方法。虚方法是使用关键字 virtual 声明的。虚方法可以在不同的继承类中有不同的实现。对虚方法的调用是在运行时发生的。

动态多态性是通过 抽象类 和 虚方法 实现的。

下面的程序演示了这点:

  1. using System;
  2. namespace PolymorphismApplication
  3. {
  4. class Shape
  5. {
  6. protected int width, height;
  7. public Shape( int a=0, int b=0)
  8. {
  9. width = a;
  10. height = b;
  11. }
  12. public virtual int area()
  13. {
  14. Console.WriteLine("父类的面积:");
  15. return 0;
  16. }
  17. }
  18. class Rectangle: Shape
  19. {
  20. public Rectangle( int a=0, int b=0): base(a, b)
  21. {
  22.  
  23. }
  24. public override int area ()
  25. {
  26. Console.WriteLine("Rectangle 类的面积:");
  27. return (width * height);
  28. }
  29. }
  30. class Triangle: Shape
  31. {
  32. public Triangle(int a = 0, int b = 0): base(a, b)
  33. {
  34.  
  35. }
  36. public override int area()
  37. {
  38. Console.WriteLine("Triangle 类的面积:");
  39. return (width * height / 2);
  40. }
  41. }
  42. class Caller
  43. {
  44. public void CallArea(Shape sh)
  45. {
  46. int a;
  47. a = sh.area();
  48. Console.WriteLine("面积: {0}", a);
  49. }
  50. }
  51. class Tester
  52. {
  53.  
  54. static void Main(string[] args)
  55. {
  56. Caller c = new Caller();
  57. Rectangle r = new Rectangle(10, 7);
  58. Triangle t = new Triangle(10, 5);
  59. c.CallArea(r);
  60. c.CallArea(t);
  61. Console.ReadKey();
  62. }
  63. }
  64. }

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

  1. Rectangle 类的面积:
  2. 面积:70
  3. Triangle 类的面积:
  4. 面积:25

C++与C#的多态的更多相关文章

  1. Java中的多态

    1.多态:具有表现多种形态的能力的特征 父类: public abstract class Animal {public abstract void Say();} 子类: public class ...

  2. C# 工厂模式+虚方法(接口、抽象方法)实现多态

    面向对象语言的三大特征之一就是多态,听起来多态比较抽象,简而言之就是同一行为针对不同对象得到不同的结果,同一对象,在不同的环境下得到不同的状态. 实例说明: 业务需求:实现一个打开文件的控制台程序的d ...

  3. C#非常重要基础之多态

    前几天看了一位同志的博客,写的是关于他自己去支付宝面试的经历.过程大体是这样的:问答的时候,前面部分,作者都应答如流,说起自己经验如何之丰富,最后面试官问了作者一个问题:请简述多态的概念和作用.结果这 ...

  4. C++多态详解

    多态是面向对象的程序设计的关键技术.多态:调用同一个函数名,可以根据需要但实现不同的功能.多态体现在两个方面,我们以前学过的编译时的多态性(函数重载)和现在我们这一章将要学习的运行时的多态性(虚函数) ...

  5. 【那些年关于java多态应用】

    1.多态:具有表现多种形态的能力的特征 父类: public abstract class Animal { public abstract void Say();} 子类: public class ...

  6. JAVA多态

    多态是指当系统A访问系统B的服务时,系统B可以通过多种方式来提供服务,而这一切对系统A是透明的.比如动物园的饲养员能够给各种各样的动物喂食.下图显示了饲养员Feeder,食物Food和动物Animal ...

  7. C#多态“说来也说”——逻辑层BLL中的多态使用

    本文版权归博客园和作者吴双本人共同所有.欢迎转载,转载和爬虫请注明原文地址 http://www.cnblogs.com/tdws/p/5861842.html 昨天晚上,有个朋友说学了好久,依然没搞 ...

  8. java多态的理解

    面向对象语言中的类有三个特征,封装.继承.多态.封装与继承很好理解,那什么是多态呢? 1.什么是多态? 多态的定义:指允许不同类的对象对同一消息做出响应.即同一消息可以根据发送对象的不同而采用多种不同 ...

  9. java中如何实现多态

    复习基础知识 多态,就是重载和重写.重载发生在一个类中.重写发生在子类,意思就是子类重写父类相同名称的方法.刚学语言有的东西,不必搞得那么清楚,只有知道怎么用就行了,有的问题你要想真正把它搞得很懂,短 ...

  10. OC多态

    要点: 1.多种形态,引用的多种形态对于一个引用变量,可以指向任何类的对象.对于一个父类的引用(类与类之间有一种继承关系),可以指向子类,也可以指向本类,指向的类型不同.当通过此引用向对象发送消息,调 ...

随机推荐

  1. 对volatile不具有原子性的理解

    在阅读多线程书籍的时候,对volatile的原子性产生了疑问,问题类似于这篇文章所阐述的那样.经过一番思考给出自己的理解. 我们知道对于可见性,Java提供了volatile关键字来保证可见性.有序性 ...

  2. 5、Python,enumerate用法

    1.enumerate()是python的内置函数 2.对于一个可迭代(iterable)/可遍历的对象(如列表,字符串),enumerate将其组成一个索引序列,利用它可以同时获得索引和值 3.多用 ...

  3. 3、Python文件操作工具 xlwt 工具

    # _*_ encoding:utf-8 _*_ import xlrdimport xlwt #新建excel文件excel = xlwt.Workbook(encoding='utf-8')#添加 ...

  4. Windows环境下JDK的配置及多版本JDK切换的方法记录

    (这里记录了笔者了解的关于JDK环境配置的信息,以及针对系统上存在不同版本JDK时所尝试的解决方案.具体来说,是已安装 JDK 8 后,又安装了 JDK 9 时所遇到的问题和尝试的解决方法.这次记录以 ...

  5. 8、Spring Cloud-配置中心 Spring Cloud Config(待补充)

    8.1.Config Server 本地读取配置文件 Config Server 可以从本地仓库读取配置文件,也可以从远处 Git 仓库读取.   本地仓库是指将所有的配置文件统 写在 Config ...

  6. 面试准备——(五)Jmeter

    面试中遇到的问题: 1. 如何使用Jmeter进行并发测试 2. 如何设置并发量为1000 3. 如果http请求每个都不一样,如何配置 4. 如何设置sessionID 一.安装配置 1. 在Ter ...

  7. PHP文档

    http://php.net/manual/zh/function.socket-close.php

  8. openssl生成iis需要的pfx格式的证书

    合成.pfx证书 将私钥文件(server.key)和服务器crt证书文件(server.crt ),放到openssl安装目录的bin目录下. 控制台也进到此目录下,然后执行下面指令. openss ...

  9. STM32F103 ucLinux开发之三(内核启动后不正常)(完结)

    STM32F103 ucLinux内核没有完全启动 从BOOT跳转到内核后,执行一长段的汇编语言,然后来到startkernel函数,开启C语言之旅. 但是内核输出不正常,如下所示: Linux ve ...

  10. 【算法学习】AVL平衡二叉搜索树原理及各项操作编程实现(C语言)

    #include<stdio.h> #include "fatal.h" struct AvlNode; typedef struct AvlNode *Positio ...