C++ 面向对象(四)—— 多态 (Polymorphism)

基类的指针(Pointers to base class)

继承的好处之一是一个指向子类(derived class)的指针与一个指向基类(base class)的指针是type-compatible的。 本节就是重点介绍如何利用C++的这一重要特性。例如,我们将结合C++的这个功能,重写前面小节中关于长方形rectangle 和三角形 triangle 的程序:

// pointers to base class
#include <iostream.h>
class CPolygon {
protected:int width, height;
public:void set_values (int a, int b) {width=a; height=b;}
};
class CRectangle: public CPolygon {
public:int area (void) {return (width * height);}
};
class CTriangle: public CPolygon {
public:int area (void) {return (width * height / 2);}
};
int main () {CRectangle rect;CTriangle trgl;CPolygon * ppoly1 = &rect;CPolygon * ppoly2 = &trgl;ppoly1->set_values (4,5);ppoly2->set_values (4,5);cout << rect.area() << endl;cout << trgl.area() << endl;return 0;
}
20
10

在主函数 main 中定义了两个指向class CPolygon的对象的指针,即 *ppoly1 和 *ppoly2。 它们被赋值为rect 和 trgl的地址,因为rect 和 trgl是CPolygon 的子类的对象,因此这种赋值是有效的。

使用*ppoly1 和 *ppoly2 取代rect 和trgl 的唯一限制是*ppoly1 和 *ppoly2 是CPolygon* 类型的,因此我们只能够引用CRectangle 和 CTriangle 从基类CPolygon中继承的成员。正是由于这个原因,我们不能够使用*ppoly1 和 *ppoly2 来调用成员函数 area(),而只能使用rect 和 trgl来调用这个函数。

要想使CPolygon 的指针承认area()为合法成员函数,必须在基类中声明它,而不能只在子类进行声明(见下一小节)。

 

虚拟成员(Virtual members)

如果想在基类中定义一个成员留待子类中进行细化,我们必须在它前面加关键字virtual ,以便可以使用指针对指向相应的对象进行操作。

请看一下例子:

// virtual members
#include <iostream.h>
class CPolygon {protected:int width, height;public:void set_values (int a, int b)  {width=a;height=b;}virtual int area (void) { return (0); }
};
class CRectangle: public CPolygon {public:int area (void) { return (width * height); }
};
class CTriangle: public CPolygon {public:int area (void) {return (width * height / 2);}
};
int main () {CRectangle rect;CTriangle trgl;CPolygon poly;CPolygon * ppoly1 = &rect;CPolygon * ppoly2 = &trgl;CPolygon * ppoly3 = &poly;ppoly1->set_values (4,5);ppoly2->set_values (4,5);ppoly3->set_values (4,5);cout << ppoly1->area() << endl;cout << ppoly2->area() << endl;cout << ppoly3->area() << endl;return 0;
}
20
10
0

现在这三个类(CPolygon, CRectangle 和 CTriangle) 都有同样的成员:width, height, set_values() 和 area()。

area() 被定义为virtual 是因为它后来在子类中被细化了。你可以做一个试验,如果在代码种去掉这个关键字(virtual),然后再执行这个程序,三个多边形的面积计算结果都将是 0 而不是20,10,0。这是因为没有了关键字virtual ,程序执行不再根据实际对象的不用而调用相应area() 函数(即分别为CRectangle::area(), CTriangle::area() 和 CPolygon::area()),取而代之,程序将全部调用CPolygon::area(),因为这些调用是通过CPolygon类型的指针进行的。

因此,关键字virtual 的作用就是在当使用基类的指针的时候,使子类中与基类同名的成员在适当的时候被调用,如前面例子中所示。

注意,虽然本身被定义为虚拟类型,我们还是可以声明一个CPolygon 类型的对象并调用它的area() 函数,它将返回0 ,如前面例子结果所示。

 

抽象基类(Abstract base classes)

基本的抽象类与我们前面例子中的类CPolygon 非常相似,唯一的区别是在我们前面的例子中,我们已经为类CPolygon的对象(例如对象poly)定义了一个有效地area()函数,而在一个抽象类(abstract base class)中,我们可以对它不定义,而简单得在函数声明后面写 =0 (等于0)。

类CPolygon 可以写成这样:

// abstract class CPolygon
class CPolygon {protected:int width, height;public:void set_values (int a, int b) {width=a;height=b;}virtual int area (void) =0;
};

注意我们是如何在virtual int area (void)加 =0 来代替函数的具体实现的。这种函数被称为纯虚拟函数(pure virtual function),而所有包含纯虚拟函数的类被称为抽象基类(abstract base classes)。

抽象基类的最大不同是它不能够有实例(对象),但我们可以定义指向它的指针。因此,像这样的声明:

CPolygon poly;

对于前面定义的抽象基类是不合法的。

然而,指针:

CPolygon * ppoly1;
CPolygon * ppoly2

是完全合法的。这是因为该类包含的纯虚拟函数(pure virtual function) 是没有被实现的,而又不可能生成一个不包含它的所有成员定义的对象。然而,因为这个函数在其子类中被完整的定义了,所以生成一个指向其子类的对象的指针是完全合法的。

下面是完整的例子:

// virtual members
#include <iostream.h>
class CPolygon {protected:int width, height;public:void set_values (int a, int b) {width=a;height=b;}virtual int area (void) =0;
};
class CRectangle: public CPolygon {public:int area (void) { return (width * height); }
};
class CTriangle: public CPolygon {public:int area (void) {return (width * height / 2);}
};
int main () {CRectangle rect;CTriangle trgl;CPolygon * ppoly1 = &rect;CPolygon * ppoly2 = &trgl;ppoly1->set_values (4,5);ppoly2->set_values (4,5);cout << ppoly1->area() << endl;cout << ppoly2->area() << endl;return 0;
}
20
10

再看一遍这段程序,你会发现我们可以用同一种类型的指针(CPolygon*)指向不同类的对象,至一点非常有用。 想象一下,现在我们可以写一个CPolygon 的成员函数,使得它可以将函数area()的结果打印到屏幕上,而不必考虑具体是为哪一个子类。

// virtual members
#include <iostream.h>
class CPolygon {protected:int width, height;public:void set_values (int a, int b) {width=a;height=b;}virtual int area (void) =0;void printarea (void) {cout << this->area() << endl;}
};
class CRectangle: public CPolygon {public:int area (void) { return (width * height); }
};
class CTriangle: public CPolygon {public:int area (void) {return (width * height / 2);}
};
int main () {CRectangle rect;CTriangle trgl;CPolygon * ppoly1 = &rect;CPolygon * ppoly2 = &trgl;ppoly1->set_values (4,5);ppoly2->set_values (4,5);ppoly1->printarea();ppoly2->printarea();return 0;
}
20
10

记住,this 代表代码正在被执行的这一个对象的指针。

抽象类和虚拟成员赋予了C++ 多态(polymorphic)的特征,使得面向对象的编程object-oriented programming成为一个有用的工具。这里只是展示了这些功能最简单的用途。想象一下如果在对象数组或动态分配的对象上使用这些功能,将会节省多少麻烦

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/401823.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

五年后存储会是什么样子

原文&#xff1a;http://chucksblog.emc.com/chucks_blog/2013/06/what-storage-might-look-like-in-five-years.html注明&#xff1a;本文内容基于 VMware VSAN beta 版本撰写&#xff0c;请访问http://www.vmware.com/products/virtual-san/获得有关正式版本的更新信息。有时…

C++ 标准函数库 —— 文件的输入输出 (Input/Output with files)

C 通过以下几个类支持文件的输入输出&#xff1a; ofstream: 写操作&#xff08;输出&#xff09;的文件类 (由ostream引申而来)ifstream: 读操作&#xff08;输入&#xff09;的文件类(由istream引申而来)fstream: 可同时读写操作的文件类 (由iostream引申而来) 打开文件(Ope…

java.lang.NoClassDefFoundError: javax/transaction/Synchronization (jUnit测试报错)

测试hibernate报错原因项目缺少包在 hibernate 解压目录下找到 jta.jar 文件往项目中添加该 jar 包&#xff0c;即可解决添加方法&#xff1a;【右击项目】-->【构建路径】....来自为知笔记(Wiz)转载于:https://www.cnblogs.com/zhanyao/p/3711322.html

C++ 高级篇(五)—— 预处理指令

预处理指令是我们写在程序代码中的给预处理器(preprocessor)的 命令&#xff0c;而不是程序本身的语句。预处理器在我们编译一个C程序时由编译器自动执行&#xff0c;它负责控制对程序代码的第一次验证和消化。 所有这些指令必须写在单独的一行中&#xff0c;它们不需要加结尾的…

yarn oom问题一例

线上部分job运行失败&#xff0c;报OOM的错误:因为是maptask报错&#xff0c;怀疑是map数量过少&#xff0c;导致oom&#xff0c;因此调整参数&#xff0c;增加map数量&#xff0c;但是问题依然存在。看来和map的数量没有关系。通过jobid查找jobhistory中对应的日志信息&#x…

C++ 高级篇(三)—— 出错处理

本节介绍的出错处理是ANSI-C 标准引入的新功能。如果你使用的C 编译器不兼容这个标准&#xff0c;则你可能无法使用这些功能。 在编程过程中&#xff0c;很多时候我们是无法确定一段代码是否总是能够正常工作的&#xff0c;或者因为程序访问了并不存在的资源&#xff0c;或者由…

kickStart脚本

kickstart是什么 许多系统管理员宁愿使用自动化的安装方法来安装红帽企业 Linux.为了满足这种需要,红帽创建了kickstart安装方法.使用kickstart,系统管理员可以创建一个文件,这个文件包含了在典型的安装过程中所遇 到的问题的答案. Kickstart文件可以存放于单一的…

C++ 高级篇(二)—— 名空间 (Namespaces)

通过使用名空间(Namespaces)我们可以将一组全局范围有效的类、对象或函数组织到一个名字下面。换种说法&#xff0c;就是它将全局范围分割成许多子域范围&#xff0c;每个子域范围叫做一个名空间(namespaces). 使用名空间的格式是&#xff1a; namespace identifier{namespace-…

32位CentOS系统安装kernel-PAE支持4g以上内存

转载自http://hi.baidu.com/yinhuama/item/31e572bb0a38f2ea4ec7fd7f 32位centos系统默认安装i386的内核不支持4g的内存 用yum安装kernel-PAE yum -y install kernel-PAE>vim /boot/grub/grub.conf修改设置为默认启动&#xff0c;default1改为default0 重启服务器 reboot再执…

C++ 高级篇(一)—— 模板(Templates)

模板(Templates)是ANSI-C 标准中新引入的概念。如果你使用的 C 编译器不符合这个标准&#xff0c;则你很可能不能使用模板。 函数模板( Function templates) 模板(Templates)使得我们可以生成通用的函数&#xff0c;这些函数能够接受任意数据类型的参数&#xff0c;可返回任意类…

Java笔试之Singleton

单例模式的出现&#xff0c;其主要的目的是为了使一个类只有一个实例存在。 实现单例模式主要有两种方式&#xff08;我所知道的&#xff09; 饿汉式&#xff1a;不管有没有需要&#xff0c;都在内部先new出一个实例public class Singleton{//私有的默认构造子 private Single…

Linux--文件结构体struct file

代码:struct file {   struct file *f_next&#xff0c;**f_pprev;   struct dentry *f_dentry;   struct file_operations *f_op;   mode_t f_mode;   loff_t f_pos;   unsigned int f_count&#xff0c;f_flags;   unsigned long f_reada&#xff0c;f_…

PPT到底是天使还是魔鬼?

说老实话&#xff0c;我非常不喜欢那些PPT的培训者一味地鼓吹PPT的重要性&#xff0c;视觉化思维的重要性&#xff0c;PPT在演讲中的重要性等等&#xff0c;我很清楚他们这么说是因为他们必须这么说&#xff0c;因为如果不把PPT的重要性强调一下&#xff0c;谁来上他的课呢&…

ultraedit正则表达式

一般使用ultraedit中的Perl风格的正则表达式&#xff0c;下面是perl正则的基本语法 perl中的元字符如下&#xff1a; ^ 表示一行的开头&#xff1b; $ 表示一行的结尾&#xff1b; ( ) 表示一个匹配块的&#xff0c;可以对匹配上的块通过$1,$2...进行读取&#xff0c;…

微软云介绍

微软云介绍 http://msdn.microsoft.com/zh-cn/ff380142 什么是云开发&#xff1f;&#xff08;概述&#xff09; 云计算是指远程运行并通过 Internet 访问的计算机和应用程序。在云计算中&#xff0c;虚拟机在大型数据中心中运行&#xff0c;并取代了物理 PC 和服务器。通过将许…

IO端口和IO内存的区别及分别使用的函数接口

IO端口和IO内存的区别及分别使用的函数接口 每个外设都是通过读写其寄存器来控制的。外设寄存器也称为I/O端口&#xff0c;通常包括&#xff1a;控制寄存器、状态寄存器和数据寄存器三大类。根据访问外设寄存器的不同方式&#xff0c;可以把CPU分成两大类。一类CPU&#xff08…

DHCP服务开启了,为什么老是网络冲突

<b><a target"_blank" href"http://www.hnbkhb.com/ " >河南</a><a target"_blank" href"http://www.hnzzfk.com/ " >河南</a><a target"_blank" href"http://www.0371fkyy.com/ &qu…

Linux进程状态解析之R、S、D、T、Z、X

Linux进程状态解析之R、S、D、T、Z、X&#xff1b;Linux是一个多用户&#xff0c;多任务的系统&#xff0c;可以同时运&#xff1b;众所周知&#xff0c;现在的分时操作系统能够在一个CPU上运&#xff1b;在linux系统中&#xff0c;每个被运行的程序实例对应一个&#xff1b;Li…

Win8 HTML5与JS编程学习笔记(二)

近期一直受到win8应用的Grid布局困扰&#xff0c;经过了半下午加半个晚上的奋斗&#xff0c;终于是弄明白了Grid布局方法的规则。之前我是阅读的微软官方的开发教程&#xff0c;书中没有详细说明CSS3的布局规则&#xff0c;自己鼓捣了半天也是一头雾水&#xff0c;于是又找到了…

flsah的分类

1. flash按照内部存储结构不同&#xff0c;分为两种&#xff1a;nor flash和nand flash。 nor flash&#xff1a;像访问SDRAM一样&#xff0c;按照数据/地址总线直接访问, 可写的次数较少&#xff0c;速度也慢&#xff0c;由于其读时序类似于SRAM&#xff0c;读地址是线性结构&…