3.1 标准 C++精讲

优质
小牛编辑
131浏览
2023-12-01

掌握标准 C++的基础知识和技能是使用 Qt 进行编程的前提,虽然 Qt 也支持其他的语言 扩展(比如 Java、Python 等),但 Qt 的基础和努力方向仍然是以 C++语言为主,所以读者 朋友一定要掌握标准 C++。

3.1.1 程序设计语言介绍

1.软件

计算机内部所有能够存储的各种数据和能够执行的各种程序都称为软件。而程序一词 经常有两种理解:(1)由程序员编写的源代码;(2)可执行的软件。

程序通常可以分为以下几类:

(1) 操作系统(Openation System)

为用户管理计算机软硬件的程序。例如 DOS、Windows 98、Windows XP、Windows CE、 UNIX、Linux、Mac OS X、BSD、Solaris 等。

实际上,操作系统包含很多可执行程序,这些程序组合在一起,完成一个或几个特定 的任务。这些程序的根本目的在于有效的组织计算机的硬件资源,并为用户提供一个访问硬 件的友好界面。通常也可以将其称为系统软件。

(2) 应用软件(Applications)

在操作系统下执行的,具有特殊用处的程序,如字处理软件 Microsoft Word、游戏软件、财务软件等。这部分程序,也就是程序员主要的服务方向,也是软件设计中商业利润比较大的一部分内容。

(3) 应用程序开发环境(Application Development Environment)

协助程序员开发应用程序的特殊程序,如 Microsoft Visual Studio、Eclipse 等。

2.程序语言

程序语言是人与计算机交流的工具。通过程序语言我们可以编写程序,控制计算机执 行相应任务。到目前为止,先后出现了 4 代程序语言。

第 1 代语言:机器语言(machine language)

计算机执行的每一个操作都是由一组特定的二元指令所指定,称为操作码。通常将这些操作码称为机器语言。该语言不仅难于读写,撰写复杂,而且非常容易出错。

第 2 代语言:汇编语言(assembly language)

以接近英文和数学式的方式写作程序,它的语意与机器语言有一对一的对应关系。该语言能发挥特定机器的硬件功能,编译后的程序运行速度快、效率高,但因仍与机器硬件相关,编写仍然较困难。

第 3 代语言:高级程序语言(high -level programming language)

包括 Fortran、Pascal、C、C++、Java、Basic、C#等,用这些语言编写应用程序的时 候,通常不需要知道 CPU 执行的细节。这些语言都有自己的特色,能胜任某一方面的程序 设计。例如 Fortran 用于科学及工程的方程式运算,而 C 语言具备汇编语言的优点,可以 直接进行位运算,而且有高度的结构性,代码相对容易维护,可理解性大大提高。 C++不仅 继承了 C 的优点,而且引入了面向对象程序设计的思想。

第 4 代语言:特殊用途语言

使用在特殊的环境中,便于非专业程序设计人员使用的语言,例如 Perl、SQL、MATLAB 等,它们通常都不需要声明变量,而且有很多现成的功能可以套用。

3.编程思想

编程思想逐渐由结构化编程发展到面向对象编程。 结构化编程的主要思想是把问题细化,即把现实中的目标分解成一系列的任务,对每一个任务再进行分解,直到每个任务都能被解决为止。这是处理复杂问题的一种非常成功的方法。即使是现在,在处理很多问题上仍会经常用到这个解决问题的思想。当然,这种编程 思想存在着问题。首先,它将数据结构从函数中分离出来,这使得程序员在设计程序的时候不得不把一个事物的属性与方法分离开考虑,于是就很难真实的表现现实生活中的模型。其 次,代码的可重用性不高。虽然,现在用的很多函数都是用 C 语言编写的,但是,现在需 要的可重用的代码已经不再是这个层次上的代码,而是集中了很多功能和数据的完整的组件,并且要保证其可维护。

面向对象的编程思想满足以上的种种需求,它提供一种方法,可实现软件组件的可重 用性,并将数据与操作数据的任务结合起来。

面向对象编程的实质就是将“对象”作为模型,而不是“数据”。用作模型的对象可 以是屏幕上的界面,如按钮、列表框,也可以是现实世界中的对象,如自行车、人等。

面向对象的编程思想必须要涉及到封装、继承、多态性这 3 种技术,C++完全支持它们。

(1) 封装

实际的事物有很多组成部分,描述一个事物也可以从不同的角度出发,从程序设计的 角度出发,需要两个元素-数据和函数,当从这两个元素出发去描述一个事物时,会看到: 一个事物本身具有一些属性,相应的存在一些行为能改变其中的一些属性。要用程序中的对 象来代表这些实际的事物,于是,用数据代表事物的属性,用函数反映事物的行为。这样, 与该对象相关的数据和行为就被封装在这个对象里了。

例如:一个人有肤色、身高、体重等属性,有自己的说话、跑、跳等行为。但有时, 属性并不是实际存在的一个事物特征的反映,它可能是一种人为抽象出来的状态的反映,例 如一个人有静止、说话、跑步、睡觉 4 种状态,可以定义一个属性来表示当前这个人是处 于什么状态。假设这个人处于百米跑起跑前的静止状态,当发令枪响起,这个人开始跑(即 发出跑的动作),同时状态属性发生变化,由静止状态变为跑步状态;还有其他的属性也随 着这个动作的发出而发生变化,由静止状态变为跑步状态;还有其他的属性也随着这个跑的 动作的发出而发生变化,如心跳频率加快、移动速度变快等。这个过程即反映了行为改变属 性这一特征。

程序并不一定要完全模拟实际的事物。在系统分析时,希望设计结果能真实反映实际 问题,但是,也没有必要对涉及到的所有的事物都建立模型。下面针对系统分析过程中可能 遇到的问题举例。一个顾客染发,染发的动作会改变头发的颜色属性。显然,染发的动作由 理发师发出,但在程序中是否真的由理发师发出这个动作还需要根据具体问题再进行分析。

I. 当具体问题中理发师的存在仅仅完成一个染发的作用,那么要考虑理发师是否有存 在的必要,如果没必要,就果断的删除这个对象,把染发的动作交给顾客自己来完成。

II. 当具体问题是为了描述理发师的行为状态变化,那么可能就把这个染发的动作交给 理发师来完成。

该例子也说明,行为交给哪个对象来完成,是需要就具体问题进行分析而得到的,关 于这个问题可以参看下列规则:

  • 如果行为影响或修改了一个对象,最好把该行为交给目标对象(而不是动作的发起 者 )。
  • 如果行为涉及到多个对象以及它们之间的关联,从中找出处于中心地位的对象,将 行为将给该对象完成。

这两个规则只是经验的反映,经验不能反映所有的情况,也有可能把行为交给其他对象来完成也是合理的,这需要根据实际情况进行判断。

(2) 继承

这是代码重用的很有效的方法,新的类可以通过继承原有类,并选择性的增加或修改其中的属性或行为,以达到利用原有类的目的。现介绍一下代码重用的方式。

注意,类是与对象相关联的概念,将在稍后介绍。

I. 源代码剪贴

最原始的形式,缺点很多。首先是复制或修改原有代码可能会出错,其次需要对源代码有一定程度的了解。另外,存在严重的配置管理问题,人们几乎无法跟踪源代码多次修改重用的过程。

II. 源代码包含

许多程序语言都提供了包含库中源代码的机制。使用这种重用形式时,配置管理问题有所缓解,因为修改了库中源代码之后,所有包含它的程序自然都必须重新编译。

III. 继承

利用继承机制重用类库中的类时,无需修改已有的代码,就可以扩充或具体化原有类,因此,基本上不存在配置管理的问题。

(3) 多态

允许使用相同的接口,与各种不同的派生类定义出来的对象交互,能够产生正确的行为。

例如,中国人说汉语,美国人说英语。 “说”的动作相同,却有着不同的内容-汉语和英语;对于不同类型的电视机,都可以使用相同的动作:单击【播放】按钮,开始播放电 视频道,显然不同类型的电视机显示的方法会有所区别,但没有必要关心这些,人们看电视的目的已经达到了。

3.1.2 C++语法基础知识

1.预处理知识

在阅读本节之前,读者最好看看 C++这方面的专著。这里只讲一些常用的知识,并不对 C++作全面深入的讲解。

C++程序由对象、函数、变量及其他组件组成。 从最简单的程序讲起:

#include <instream.h>
int main()
{
    cout<<”Hello World\n”;
    return 0;
}

这是一个 Console 程序,撰写 Console 程序时需要注意:主程序为 main 可以使用 C Runtime 函数和不涉及 GUI 的 Win32 API 函数。撰写 Console 程序是学习 C++的第一步。进入 Console 模式进行编程,使用 VC++ 6.0,操作步骤如下:

(1) 选择【File】菜单中的【New】菜单项,在弹出的对话框中选择【Projects】标 签;再选择“Win32 Console Application“程序,然后输入工程名称,单击【OK】按钮; 在接下来的对话框中,为了脱离 VC 提供的代码支持,选择“an empty project”,然后单 击【Finish】按钮,会得到一个不包含任何工程文件的工程。之后,要加入包含着主函数的 头文件。

(2) 选择【File】菜单中的【New...】子菜单,在弹出的对话框中选择【Files】标 签,再选择“C++ Source File”,选中“Add to Project”项,选中(1)中的工程名 称。接下来,确定文件的名称,单击【 OK】按钮,于是就得到了需要的文件,同时可以在这 个空文件里键入上述代码。

(3) 运行程序,它会弹出一个对话框提示没有可执行程序,询问是否创建可执行程序。 单击【是】即可,然后 VC 开始编译,链接目标文件。如果没有意外的话,会得到一个 DOS 窗口,窗口内有如下的输出:

Hello World
Press any key to Continue

最后一句话并不是源代码反映的内容,这是由编译器提供的,方便查看输出内容的提

示。如果读者想看程序真正的输出结果,就进入命令行下(即以前的 DOS 窗口下),运行 该程序,读者就可以看到 Hello World,而没有其他的信息。

#include<iostream.h>

该语句的意思是:将文件包含到当前的文件中,符号 #是预处理 标志。

注意,每次启动编译器时,先运行预处理器。预处理器浏览源代码,找到以 #开头的 行,处理这些行,预处理的作用是改变源代码的文本。结果生成一个新的源代码文件-一个 通常看不到的临时文件,但读者可以指定编译器保存它,这样,读者可以在感兴趣的时候或 者需要的时候检查它。编译器不读取最初的源代码文件,它读取预处理器输出的结果并编译 该文件,最终生成可执行程序。

初步介绍预处理作用如下,以便读者了解预处理的概念:

I. 包含另一个文件(通常是头文件),为了引入需要的代码。

II. 定义符号,祈祷开关作用,可以根据机器的情况、操作系统的情况及用户的需求来 决定哪部分代码有效,例如,如果要在 Win32 的环境下编程,那么,就定义 Win32 这个符 号。

III. 定义宏,简化常用的数据,或者简化复杂的函数声明、定义的过程。

预处理的功能不止这些,以上是本书程序中略有涉及的内容。

正式的程序从 main 函数开始,每一个 C++程序都有一个 main 函数。函数是指能实现一 个或多个功能的代码块。通常函数是由其他函数调用或激活,而 main 属于特殊情况。程序 在开始的时候自动调用 main。从本质上讲,也是被调用,不过,那些都不是现在需要关心 的事情。

所有的函数都以左大括号开始,以右大括号结束。两个大括号之间是函数体。 使用对象 cout 将一个字符串打印到屏幕上。最后返回 0。

这些就是一个 C++程序的基本轮廓。下面开始讲解+的基本语法。 2.基本数据类型

在任何一台计算机中,每种变量类型都占据一定量的内存单元。但并不是每种变量类 型占的内存大小在每台计算机上都相同,一个整型变量在一台机器上可能是 2 个字节,而在另一台机器上可能是 4 字节,但对任意一台机器而言,这个值是确定的。字符型变量通常只有 1 个字节。

在大多数计算机上,短整型是 2 个字节,长整型是 4 个字节,而整型可能是 2 个字节也可能是 4 个字节。编程语言并没有对此做出精确的定义,它定义的是短整型必须小于或 等于整型的大小,整型必须小于或等于长整型的大小。

整型的大小是由读者所使用的处理器( 16 位还是 32 位)和编译器决定的。例如,在使 用 Visual C++4.0 及以上版本的 32 位 Intel x86 计算机上,整型为 4 字节。详细的变量 类型说明如表 3-1 所示。

表 3-1 C++变量类型说明

类型大小
bool1 字节true 或 false
unsigned short int2 字节0~65535
short int2 字节-32768~32767
unsigned long int4 字节0~4294967295
long int4 字节-2147483648~2147483647
int(16 位)2 字节-32768~32767
int(32 位)4 字节-2147483648~2147483647
unsigned int(16 位)2 字节0~65535
unsigned int(32 位)4 字节0~4294967295
char1 字节256 个字符
float1 字节1.2e-38~3.4e38
double8 字节2.2e-308~1.8e308

3.表达式和语句

在 C++中,语句用于控制程序的执行顺序、计算表达式的值,或者什么都不做(空语 句)。

所有的 C++语句都以分号结尾,即使是空语句也是如此。 下面是典型的语句:

value = value2 – value4;

在 C++中任何一个计算值的操作都可称为表达式,表达式总是能够返回一个值。表达式可以简化到 1,2,3 这样的整数,也可以是 a+b 这样的形式,重要的是它能返回一个值。

运算符是一种能使编译器进行某项操作的符号。运算符作用于操作数,在 C++中所有操 作数都是表达式。

(1) 赋值运算符(=):使赋值运算符左边的操作数的值改变为赋值运算符右边的值。 例如:

value = 20;

(2) 数学运算符:加(+)、减(-)、乘(*)、除(/)、取模(%)。取模运算符(%)就是求整型除法的余数。

I. 一些特殊的运算符:如,+=-=*=/=%=。 例如:

a+=2; 等价于 a=a+2;

a-=2; 等价于 a=a-2;

a*=2; 等价于 a=a*2;

a/=2; 等价于 a=a/2;

a%=2; 等价于 a=a%2;

II. 单目运算符:如++、--等。

a++;++a; 等价于 a=a+1;

a--;--a; 等价于 a=a-1;

x=a++; 等价于两步操作:先是 x=a;然后是 a=a+1;

x=++a; 等价于两步操作:先是 a=a+1;然后是 x=a;

在复杂的表达式之中,多个运算符同时出现,不同的符号有不同的执行优先级。例如:a=8-32,对于该表达式,由于号的优先级大于-号,所以先执行 32,然后执行 8-6,最后执行 a=2。可以通过添加括号()来改变运算顺序。将原式改成 a=(8-3)2,这时,先 执行 8-3,再执行 5*2,最后执行 a=10。

(3) 关系运算符:用来对两个量进行比较的(等于、大于或小于)符号。每条关系语句 的值要么为真要么为假,每个表达式都可以按真假来求值。凡是数值运算结果为 0 的表达 式返回假,否则返回真,也就是返回 False 或 True。C++基本的关系运算符如表 3-2 所示。

表 3-2 关系运算符说明

名称运算符例子
等于==1==1true
1==2false
不等于!=1!=1false
1!=2true
大于>1>2false
2>1true
大于等于>=1>=2fasle
1>=1true
小于<1<2true
2<1false
小于等于<=1<=2true
2<=1false

(4) 逻辑运算符:用于判断操作数之间逻辑关系的运算符, C++基本的逻辑运算符如表3-3 所示。

表 3-3 逻辑运算符

运算符符号例子
与(AND)&&表达式 1&&表达式 2
或(OR)||表达式 1||表达式 2
非(NOT)!!表达式

(5) 条件运算符(?:)

条件运算符(?:)是 C++中唯一的三目运算符。其格式如下:

(表达式 1)?(表达式 2):(表达式 3)

它的意思是:如果表达式 1 的值为真,就返回表达式 2 的值;否则就返回表达式 3 的值。通常,这个值将赋给某个变量。

例如:

c=(a&gt;b)?a:bj;

等价于

if(a&gt;b)
{
    c = a;
}
else
{
    c=b;
}

4.函数

函数实际上是能够对数据进行处理并返回一个值的子程序。每个 C++程序都至少有一个 函数 main。当程序启动时,系统自动调用 main 函数。main 函数可调用其他的函数,其中 一些函数还可以再调用其他函数。每个函数都有自己的名字,当程序读到函数名时,程序执 行就转到函数体。这个过程称作“调用函数”。当函数执行完后,程序又跳回到函数名所 在行的下一行继续执行。设计得好的函数能执行特定的易于了解的任务。对于复杂的任务, 应该将其分成多个函数来完成,这些函数可以被程序依次调用。

函数通常有两种类型:用户定义函数和内置函数。用户定义函数是由用户自己编写的 函数。内置函数则是编译器软件包的一部分-由开发商提供给用户使用。

(1) 函数的声明

在使用函数时,必须先声明再定义。声明告诉编译器该函数的名称、返回值类型以及参数。定义则告诉编译器该函数的功能是什么。如果不声明,任何函数都不能被其他函数调用。函数的声明又称为函数原型。

有 3 种函数的声明:

I. 将函数原型写在某个文件中,再用#include 将其包含到程序中

II. 将函数原型写到使用该函数的文件中

III. 在函数被其他函数使用前定义该函数,这样做时,函数定义将作为声明。

实际上,大家使用的许多内置函数已经将它们的函数原型写到了用 #include 包含在程序使用的头文件内。对于读者自己编写的函数,必须包含该原型。函数原型也是一条语句,也就是说它以分号结尾。它由函数的返回值类型和函数标识组成。函数标识包括函数名和参 数列表。参数列表包含函数的所有参数及其类型的列表,这些参数由逗号分开。

函数原型与它的定义必须在返回类型和标识上完全相符。如果不相符,就会有编译错 误。不过,请注意,函数原型中不必包含参数名,而只需要参数类型。例如,像这样的函数 类型就完全合乎要求:

long Area(int,int);

这个原型声明了一个函数 Area,它返回一个长整型变量,它有两个参数均为整型。不过,虽然这种方式合法,但它不够好。如果在原型中加入参数名,这个函数原型就会变得清 晰得多。例如:

long Area(int length,int width);

就可以比较清楚的看到函数的作用,参数的意义。所有的函数都有返回值,如果未明确声明返回值类型,则系统自动默认为整型。不过,精确的声明每个函数的返回值类型,包 括main,可使程序更清楚。

(2) 函数的定义

函数的定义由函数头和函数体组成。函数头与函数原型很像,只是其中的参数必须有名称,而且函数头不以分号结尾。

函数体是包含在一对大括号内的一组语句。 可以这样使用函数:

int main()
{
    int areaOfRect = Area(4,5);
    cout&lt;&lt;areaOfRect;
}

函数可以调用其他函数,甚至调用自己。

5.类和对象

类与对象的关系反映在现实生活中就像是概念与实体、抽象与具体的关系。例如:提 起自行车,一定能在脑海中形成一个概念-两个轮子,可以用脚蹬的交通工具。当然,大家 不可能“骑着”这个脑海中的概念去工作,或上学,只能骑着一部真正客观存在的自行车 去工作或上学。这就是类与对象的区别,就是说对象是类的个体实例。

程序通常是用来解决实际问题的,为了方便模拟现实存在的事物以及事物之间的关 系,需要建立这样的类来模拟现实生活中相应的事物。例如:模拟猫捉老鼠的 过程,就需 要建立两个类,一个模拟猫,一个模拟老鼠。猫有个动作是追赶,老鼠有个动作是逃跑。这 样就可以动手模拟这个过程了。

要声明一个类,需要使用 C++的关键字 class,然后列出类的成员变量和成员函数,注 意类的声明要以分号结束,例如:

class Rect
{
    int width;
    int length;
    void show();
    int getArea();
};

声明 Rect 时,并没有为它分配内存。它只是告诉编译器: Rect 是什么,它包含什么数据(width,length),能做什么(show(),getArea())。他还告诉编译器 Rect 要占多大空 间,大家可以算一算,Rect 需要编译器预留多大空间呢?答案是 8 个字节,width 和 length 各占 4 个字节,加起来是 8 个,系统不会为成员函数分配存储空间。

在定义类的成员变量和成员函数时,还经常用到两个关键字 public 和 private。 类的所有成员默认时均为私有。私有成员只能通过类本身提供的成员函数内访问。公有成员则可以被该类的对象访问。如上例的 Rect 类,在默认情况下,width 和 length 都是私有成员变量,所以如果你写下了这样的代码,就会出错:

Rect rect;
rect.width = 10;
rect.length = 10;

因为在默认情况下成员变量 width 和 length 都是私有的,如果想让这两个成员变量都能够被访问,可以将它们改变为共有的,声明如下:

class Rect
{
public:
    int width;
    int length;
    void show();
    int getArea();
};

在 public 之后修饰的成员变量和函数都被定义为公有的。这样,下列语句就是合法

的:

rect.show();

要定义类的成员函数,可以在类声明函数的地方,也可以在类声明体之外的地方,但

需要 include 包含类的声明的文件。

#include”Rect.h”
int Rect::show()
{
    cout&lt;&lt;width;
    cout&lt;&lt;length;
}

类中的成员函数类似 C 函数的定义方式,不同的是,每个成员函数之前带有 “类名”和::域作用符,以表示该成员函数是属于哪个类的成员。

当然,私有的数据成员和函数并不是只有该类的成员函数才能访问到,友元函数和友 元类也能访问,例如:

class Rect { public: int width; int length; void show(); int getArea(); friend void setRectWidth(Rect* pRect); friend class Point; };

这样类 Point 和函数 setRectWidth 都可以对类 Rect 的私有成员变量和函数访问了。

6.类的继承机制

C++中允许单继承和多继承。一个类可以根据需要生成派生类。派生类根据情况继承了基类的方法,还可以定义新的方法。一个子类的每个对象包含有从父类那里继承来的数据成

员以及自己所特有的数据成员。在 C++语言中,派生类可以从一个基类派生,称为单继承; 也可以从多个基类派生,就是所谓多继承。

派生类的继承方式有“公有继承(public)”、“私有继承(private)”和“保护继 承(protected)”这 3 种常见的方式,此外还有虚继承这种方式。

这里重点介绍一下公有继承。 公有继承的特点是基类的公有成员和保护成员作为派生类的成员时,它们都保持原有的状态,而基类的私有成员仍然是私有的。

(1) 基类成员对其对象的可见性:公有成员可见,其他不可见。这里保护成员同于私有 成员;

(2) 基类成员对派生类的可见性:公有成员和保护成员可见,而私有成员不可见。这里 保护成员同于公有成员;

(3) 基类成员对派生类对象的可见性:公有成员可见,其他成员不可见。

所以,在公有继承时,派生类的对象可以访问基类中的公有成员;派生类的成员函数可以访问基类中的公有成员和保护成员。例如:

class BaseClass
{
protected:
    int x;
    int y;
};

class DerivedClass:public BaseClass
{
    void show(){cout&lt;&lt;x;cout&lt;&lt;y;}; //访问基类中受保护的数据成员
};

本节讲述了 C++程序设计的基本内容,简单的讲解了 C++中实现面向对象程序设计思想

的语法、类与对象,这些只是 C++程序中很少的一部分,希望读者能够阅读相关书籍加以巩 固提高。

3.1.3 C++高级应用-虚函数

这里重点讲一下虚函数。虚函数就是人们希望在派生类中被重新定义的函数,当我们

用基类的指针指向派生类的对象时,就能调用该派生类的虚函数。例如:

class BaseClass
{
public:
    virtual void show(){};
};

class DerivedClass:public BaseClass
{
public:
    int x;
    int y;
    void show(){cout&lt;&lt;x;cout&lt;&lt;y;};
};

函数 show()被基类声明为虚函数,那么在派生类中也就都是虚函数。使用虚函数的用

意是什么呢,请看:

BaseClass* pBaseObject = NULL;
DerivedClass DerivedObject;
pBaseObject = &DerivedObject;
pBaseObject-&gt;show();

运行后,屏幕上将显示 x,y 的值。

该例中,先定义了一个基类的指针,又定义了一个派生类的 对象,接着,用基类的指 针指向派生类的对象,最后用基类的指针调用函数 show(),这时,结果将输出 x,y 的值。

从结果上可以看出,基类的指针调用了派生类的成员函数。

需要注意以下几点: 1.在基类中声明一个成员函数为虚函数后,在它的派生类中此成员函数也是虚函数,

并且不需要在前面加关键字。

2.当指针调用函数时,如果调用的是虚函数,则根据指针指向的对象访问函数;如果 调用的是非虚函数,则指针的类型调用相应的函数;如果虚函数在派生类中没有定义,则会 自动调用基类中的函数定义。

另外,虚函数的使用需要谨慎,因为它会增加一些额外的开销,不过这点开销不足以 削弱它的强大功能,除非用户漫无目的滥用它。

此外,可以将一个虚函数声明为一个纯虚函数:

virtual void show()=0;

这么做,等于告诉编译器在对象中为函数 show 保留一个间隔,为将来从基类中派生的

函数占据一个位置。纯虚函数有以下特点:

1.纯虚函数仅仅是用来为基类的派生类中的函数保留位置

2.纯虚函数在基类中没有定义,它们被初始化为 0

3.当虚函数变成纯虚函数时,任何派生类都必须给出它自己的定义。否则编译出错。 在使用中,不能创建一个带有纯虚函数的类的对象,但允许声明含有纯虚函数的类的指针。 在程序中往往会用到这个功能。