友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
C语言实例教程(PDF格式)-第5部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
所使用的对象的层次结构,然后,直接将此结构映射到面向对象的程
序空间,而不需要做任何修改或仅需要作少量修改就可以使用面向对
象的程序设计方法来实现该结构。这时,编写程序的过程更类似于
“搭积木”,我们可以从很多途径来获得到程序所需使用的各种对
象,然后,将这些对象以一定的层次结构组合起来,从而实现程序的
逻辑结构。
2。1。4 多态和虚函数
在讲述多态之前我们先来看一个问题。仍以前面的图2。2为例,假定
我们已经定义了一个指向哺乳动物类的实例对象的指针,如下所示:
CMammal *pMammal
然后我们定义了一个人类的实例对象和一个狒狒类的实例对象,如下
所示:
CHuman Human;
CBaboon Baboon;
然后,我们可以将指针pMammal指向Human对象,在C++语言中是可以
这样做的:
pMammal=&Human;
也可以将指针指向Human对象:
pMammal=&Baboon;
考查上面的两种情况,我们假定在哺乳动物类、人类和狒狒类中都定
…………………………………………………………Page 44……………………………………………………………
义了一个Eat (吃)方法,很显然,当我们使用下面的代码来调用人类
对象和狒狒类对象的Eat方法时不会遇到什么问题:
Human。Eat();
Baboon。Eat();
但是,现在来考虑下面的代码:
pMammal…》Eat();
当pMammal指向不同的对象时,上面的代码将发生什么样的结果。很
显然,当pMammal指向一个哺乳动物类的实例对象时,上面的代码将
调用哺乳动物类的Eat方法。但是,当pMammal指向一个人类的实例对
象时,上面的代码是调用人类的Eat方法呢,还是仍然调用哺乳动物
类的Eat方法?我们期望的是前面一种情况,这就是类和对象的多态
性。我们期望,当pMammal指向不同的实例对象时,编译器将根据实
例对象的类型调用正确的Eat方法。在C++中,类的多态性是通过虚函
数来实现的。就上面的例子来说,我们将Eat方法定义为一个公有的
虚函数,这样,当pMammal指针指向一个人类的实例对象时,编译器
调用的就是人类的Eat方法,当pMammal指针指向一个狒狒类的实例对
象时,编译器调用的就是狒狒类的实例对象,从而实现了运行时的多
态。
在C++中,多态定义为不同函数的同一接口。从这个定义出发,函数
和操作符的重载也属于多态。
考虑下面定义的两个函数:
int print(char*);
int print(int);
上面的两个不同函数使用同样的函数名,在C++中,这称为函数的重
载。这时,若将一个字符指针传递给print函数,如下所示:
char *sz=〃Hello World!〃;
print(sz);
这时,编译器调用的是int print(char*),如果将一个整型变量传递
给print函数,如下所示:
int i=0;
…………………………………………………………Page 45……………………………………………………………
print(i);
则编译器调用的是int print(int)。这种根据所传递的参数的不同而
调用不同函数的情形也称作多态。
下面我们来看运算符重载的例子,在下面的过程中,我们为矩阵类重
载了运算符 “+”:
CMatrix operator +(CMatrix; CMatix);
然后使用下面的代码:
int a=1; b=1; c;
CMatrix A(3; 3; 0); B(3; 3; 1); C(3; 3);
c=a+b;
C=A+B;
在上面的例子中,我们定义了三个整型变量和三个类CMatrix的实例
变量,A、B和C均为3' 3的矩阵,其中A中全部元素均置为0,B的全部
元素均置为1。然后将 “+”运算符作用来整型变量和类CMatirx的实
例变量。这时,编译器将表达式c=a+b翻译为
c=operator +(a; b);
而将表达式C=A+B翻译为
C=operator +(A; B)
然后,根据传递参数的不同,调用不同的operator +函数。
与类和对象的多态不同,对于函数和运算符的多态,是在编译过程中
完成的,因此我们将它们称为编译时多态,与此相对,将类和对象的
多态称为运行时多态。
封装性、继承性和多态性是面向对象编程的三大特征,则开始的时
候,你也许对它们还没有非常清晰的概念,但这没有什么关系,当你
使用了一段时间的C++语言,然后再回过头来看这些概念时,你就会
发现对它们有了更深入的认识和了解。事实上,许多的C++程序正是
在使用C++语言进行编程已有相当一段时间时才对这三个概念有了正
确而清晰的认识的。
l 注意:
…………………………………………………………Page 46……………………………………………………………
l 在前面的过程中,我们多次混用了对象和类这两个术语,事实
上,它们之间是有差别的。在C++中,类是一种数据类型,它是一
组相同类型的对象的抽象。在类中定义了这一类对象的统一的接
口。而对象在C++中是指某一数据类型的一个实例,如下面的代码
所示:
l Class CBaloon;
l CBaloon Baloon1;
l CBaloon Baloon2;
l CBaloon Baloon3;
在上面的代码 中,我们声明了一个类CBaloon,这是所有狒狒
对象的抽象,然后,我们使用类来创建了三个不同的实例对
象Baloon1、Baloon2和Baloon3。
然而,尽管类和对象是两个意义不同的概念,但是,它们在
C++语言中是紧密相关的,这使得很多的文档 (包括Visual
C++的联机文档)在有些时候也不加 区分的混用这两个术语。
读者根据相应的上下文,应该能够很方便的辨明它们具体所
指的内容究竟是类还是类的实例对象。
在随后的小节中,我们将讲述如何在C++中实现上面所说的面向对象
的编程概念。
第二节 类的声明和定义
本节讲述在声明和定义类及类中的成员变量和成员函数所需注意的问
题。在本章中,我们使用了传统的C/C++编程风格,即每一个程序都
以一个名为main的主函数为入口,过去常见的MS…DOS应用程序即是这
样的。众所周知,Visual C++ 5。0不支持16位MS…DOS编程,但这并不
意味着我们必须放弃这种编程模式。事实上,我们仍然可以在Visual
C++ 5。0中编译和调试在本章中出现的所有程序,方法是使用Win32
Console Application (Win32控制台应用程序)来编译它们,Win32控
制台API提供了字符方式的应用程序的编程接口,并且,我们仍可以
使用过去所熟知的以main函数为入口的编程方式。除此之外,Win32
平台为控制台应用程序提供了虚拟的一个标准输入和输出设备 (由于
Windows程序共享屏幕和其它所有设备,因此不存在在DOS中的那种标
准输入和输出设备),这样,我们可以使用标准的C语言输入输出函数
scanf和printf,还可以使用标准的C++语言输入和输出流cin和
…………………………………………………………Page 47……………………………………………………………
cout。关于控制台应用程序的内容,可以参考本书的相关章节。
2。2。1 类及其成员变量和成员函数的声明和定义
在C++中,存在三种类类型:类、结构和联合,它们分别使用三个关
键字来声明和定义类:class、struct和union。表2。1给出了不同类
类型之间的差别。
表2。1 三种不同类型的类:类、结构和联合
类 结构 联合
声明和定义时使用 class struct union
的关键字
默认的成员访问权 私有 公有 公有
限
使用限制 无 无 同时只能使用一
个成员
由于联合的特殊性,我们将在后面的内容中讲述它。下面给出一个使
用class来定义类的示例:
class CCircle
{
public:
unsigned Radius;
CPoint Center;
};
下面我们使用关键字struct来定义类CPoint:
struct CPoint
{
unsigned x;
unsigned y;
};
…………………………………………………………Page 48……………………………………………………………
l 注意:
l 请不要忘了在类定义的第二个大括号 “}”之后加上一个分号,否
则编译时会 出现一堆莫名其妙的错,并且没有一个错误会告诉你
是在定义类的时候少加了一个分号(也许它会告诉你别的什么地方
少了一个分号,但事实上那里什么错误也没有)。也许有人认为这
并非是一个值得强调的问题,然而事实上绝大多数初学者 (甚至包
括一些有经验的程序员)都会 由于疏忽大意而犯了这样的错误,并
且在修正它的时候花费了大量的时间却依然没有发现所有的问题
实际上只是由一个小分号造成的。
细心的读者会注意到,在类CCircle的定义中我们使用了public关键
字,而在类CPoint的定义中却没有使用。这是因为在使用struct定义
的类中,成员的默认访问权限为公有,因此不需要使用public关键
字,而对于使用class关键字来定义的类,由于其成员的默认访问权
限为私有,因此必须使用关键字public来将成员Radius和Center的访
问权限设置为公有。一般来说,一个类中总是包括了一定数量的公有
成员,没有公有成员的类由于没有提供任何接口,事实上没有什么
用。
对于同一个类,可以使用关键字class来定义,也可以使用关键字
struct来定义。对于上面的例子,我们将类CCircle改用关键字
struct来定义,而将类CPoint改用关键字class来定义如下:
struct CCircle
{
unsigned Radius;
CPoint Center;
};
class CPoint
{
public:
unsigned x;y;
};
现在我们来考虑一下,如果在同一个文件中定义类CCircle和类
…………………………………………………………Page 49……………………………………………………………
CPoint,并采用如上面的代码,会遇到什么问题。我们看到,在类
CCircle中定义了类CPoint的实例对象Center,然而,在定义实例对
象Center的时候,我们还没有给出类CPoint的定义,这时,在编译时
就会出现错误。因此,我们应该采用下面的顺序:
struct CPoint
{
unsigned x;
unsigned y;
};
class CCircle
{
public:
unsigned Radius;
CPoint Center;
};
以确保在使用类CPoint这前已经对它进行了定义。如果我们在类
CPoint中添加一个成员函数IsInCircle来判断一个点是否在圆内,如
下面的代码所示:
struct CPoint
{
unsigned x;
unsigned y;
bool IsInCircle(CCircle *Circle)
{
return ( ((x…Circle…》Center。x)*(x…Circle…》Center。x)
+(y…Circle…》Center。y)*(y…Circle…》Center。y))
Radius*Circle…》Radius );
}
…………………………………………………………Page 50……………………………………………………………
};
这真是一件很不幸的事情。为什么这样说呢?请看上面的代码,我们
在IsInCircle成员函数中又使用到了类CCircle,而按照我们在前面
所给出的代码,类CCircle是在类CPoint之后进行定义的,那么,将
类CPoint放到类CCircle之后定义行不行呢?答案是不行的,不要忘
了我们还在类CCircle中定义了一个类型为CPoint的数据成员
Center。
使用类的声明可以解决上面所遇到的两难问题。为了在定义类
CCircle之前先引用类CCircle的类名,我们可以在类CPoint之前添加
如下的代码:
Class CCircle;
l 注意:
l 不能在末尾加上一对大括号,因为这样的话,上面的代码就会被
当作一个类的定义,这时,在同一文件作用域中就会出现了同一
类的两个定义,而这在C++中是不允许的,一个类可以有多个声
明,但是它只能有一个定义,对于函数也是这样。
上面的代码和类的定义的差别在于它没有一对大括号 “{}”,这样,
我们就可以在定义类CCircle先定义指向一个类CCircle的对象的指针
和引用。但是,这时CPoint的代码不能像上面那样写,这是因为在成
员函数IsInCircle中我们使用了类CCircle中定义的数据成员,而在
此之前编译器只知道用以使用一个名为CCircle的类,但是不知道这
个类中包括了什么样的成员,因此上面的代码在编译时通不过。
如果解决所遇到的新问题呢?回过头来看类CPoint的定义,我们在定
义类的同时定义了成员函数IsInCircle,联想到C语言中的函数,我
们可以很自然的想到,可不可以选声明成员函数IsInCircle,然后再
定义它呢?如果可以这样的话,我们就可以在类CPoint的定义中先声
明成员函数IsInCircle,然后在定义了类CCircle之后再添加成员
IsInCircle的定义。
事实上我们正是基于这个想法来解决问题的。我们的确可以先在类
CPoint的定义中声明成员函数IsInCircle,然后在后面添加它的定
义,完整的代码如下:
#include
class CCircle;
…………………………………………………………Page 51……………………………………………………………
struct CPoint
{
unsigned x;
unsigned y;
bool IsInCircle(CCircle *Circle);
};
class CCircle
{
public:
unsigned Radius;
CPoint Center;
};
bool CPoint::IsInCircle(CCircle *Circle)
{
return ( ((x…Circle…》Center。x)*(x…Circle…》Center。x)
+(y…Circle…》Center。y)*(y…Circle…》Center。y))
Radius*Circle…》Radius );
}
void main()
{
CPoint Center;
CPoint Point;
CCircle Circle;
Center。x=0;
Center。y=0;
Point。x=2;
…………………………………………………………Page 52……………………………………………………………
Point。y=3;
Circle。Radius=3;
Circle。Center=Center;
if (Point。IsInCircle(&Circle))
cout
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!