友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
C语言实例教程(PDF格式)-第8部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
matrix定义了一个构造函数
matrix::matrix(int);
编译器仍然不会将前面的表达式解释为
B=matrix(3)。operator*(A);
因此,将运算符函数定义为类的成员函数是不可能实现我们的要
求的,这时,我们需要将函数operator*定义为全局函数,并且,
将它作为类matrix的友元,如下所示:
matrix operator*(int k; matrix& A);
这时,编译器将前面的表达式解释为
B=operator*(3; A);
由于存在合适的函数原型,因此编译器将调用上面所定义的函数
operator*来进行运算,并将结果赋予B。
上面的叙述容易给人一种感觉,即是说将运算符函数定义为友元
函数要比将它们定义为类的成员函数好得多。事实上很多情况下
也是这样,然而,并不是所有的函数都能够被定义为友元函数,
…………………………………………………………Page 81……………………………………………………………
以下的函数只能被定义为类的成员函数:
operator=
operator()
operator''
operator…》
° 注意:
° 函数operator=只能定义为类的成员函数,但是其它的二元重
合赋值运算符,如?? 、?? 、??和??等却不受此限,请看下面的
代码 :
两个函数中C++中是不同的重载形式。
由编译器自动生成的运算符函数operator所进行的默认操作
是将两个对象中的数据进行按成员拷贝,有一点需要强调的
是,对于其中的指针成员,拷贝的是指针本身,而不是指针
所指向的内容。如果在类中使用了指针成员,这是一个必须
注意的问题,一般来说,在这种情况下,我们必须提供自定
义的拷贝构造函数和以type&为参数的赋值运算符重载函数,
否则很容易引起指针挂起的问题。
表2。5总结了不同运算符的重载方法。比较特殊的是递增运算
符+ + 和递减运算符 ,特殊的原因是它们有两种不同的
形式,即前缀形式和后缀形式。如果区别运算符 “++”和 “
”的两种不同形式呢?我们为此作如下的约定,对于前缀
形式的递增/递减运算符,以和一般的一元运算符同样的方式
将它们重载为
type& type::operator++()
表2。5 不同运算符的重载方法小结
运算符 以友元函数方式进 以成员函数方式进行重载
行重载
一元运算符@ type operator@ type operator@()
(arg)
(不包括递增运 表达式A@或@A等价于
…………………………………………………………Page 82……………………………………………………………
算符++和递减 表达式A@或@A等价 A。operator@()
运算符??) 于operator@(A)
二元运算符@ type operator@ type operator@(arg)
(arg1; arg2)
表达式A@B等价于A。operator@
表达式A@B等价于 (B)
operator@(A; B)
赋值运算符= – type& operator=(arg)
表达式A=B等价于A。operator=
(B)
函数调用运算 type operator()(arg; 。。。)
符 ()
表达式A(arg; 。。。)等价于
A。operator()(arg; 。。。)
注意:
1。 函数调用运算符被当作一个
二元运算符,然而函数调用运
算符函数的参数表却可以拥有
多个参数。
2。 函数调用运算符作用于一个
类的实例对象,而不是一个函
数名。
关于函数调用运算符可以参见
前面的类matrix的实现。假设A
是类matrix的一个实例对象,
则表达式A(1;2)返回矩阵A中第
一行第二列的元素。
下标运算符'' type operator''(arg)
表达式A'arg'等价于
A。operator''(arg)
注意:
除了可以为整数以外,下标运
算符函数的参数arg还可以为任
何类型,比如,你可以创建一
个以字符串为下标的数据列
表。
成员函数运算 type operator…》(arg)
符…》
…………………………………………………………Page 83……………………………………………………………
表达式A…》arg等价于
A。operator…》(arg)
注意:
可以重载成员选择运算符 “
》”,但不可以重载另一个成员
选择运算符 “。”。
type& operator++(type&)
或
type type::operator…()
type operator…(type&)
要注意的是,如果使用将operator++和operator……重载为全
局友元函数,则参数要使用引用类型,这是因为一般来说,
运算符 “++”和 “”都需要修改操作符本身。
对于后缀形式的递增/递减运算符,我们约定使用下面的方式
来进行重载:
type& type::operator++(int)
type& operator++(type&; int)
或
type type::operator…(int)
type operator…(type&; int)
也就是说,我们使用一个额外的整型参数来表明所需调用的
是后缀形式的递增/递减运算符。这样,表达式
++A
对于编译器等价于
A。operator++()
或
operator++(A)
…………………………………………………………Page 84……………………………………………………………
而表达式
A++
对于编译器等价于
A。operator++(0)
或
operator++(A; 0)
运算符 “”与此类似。
这样,编译器就能有效的区分前缀形式的递增/递减运算符和
后缀形式的递增/递减运算符,对于Visual C++而言,传递给
后缀形式的递增/递减运算符函数的整型参数为0,事实上,
我们可以显式的调用后缀形式的递增/递减运算符函数,如
A。operator++(3);
或
operator++(A; 3);
这时,所传递的整型参数可以不是0,而且,后缀形式的运算
符函数operator++也的确可以使用这个参数,这时, “++”
运算符看起来有点像一个二元运算符,但是,要记住,后缀
形式的运算符函数operator++只是一个约定,对于C++来说,
无论使用的是前缀形式还是后缀形式,递增/递减运算符都是
一个一元运算符,下面的表达式在Visual C++中是通不过
的:
A++3;
不要想当然的将它解释为
A。operator++(3);
或
operator++(A; 3);
n 注意:
n 由表2。3可以知道,用于动态分配内存的运算符new和
…………………………………………………………Page 85……………………………………………………………
delete也可以被重载,但是,C++对这两个运算符的默认
实现非常之好,而且,由于Win32平台的32位平坦 内存管
理机制,在系统物理内存不足时会 自动使用磁盘交换文
件,因此,在绝大多数情况下,我们不需要、也不应该
重载new和delete运算符,而且,不正确或者说不完善的
重载new和delete有可能在动态分配内存时带来难以预料
的严重后果。也鉴于这个原因,本书中不再讲述重载new
和delete运算符的细节,若读者在程序中的确需要重载
它们的话,请参考Visual C++的联机文档或其它的C++文
献。
n 重载运算符时应该遵从惯例,比如说,我们可以重载运
算符 “+”来连接两个字符串,这是合乎我们的日常思维
方式的。同样,C++允许我们使用运算符 “”来连接两
个字符串,但是,这样的重载方式不会给编程带来方
便,它们只会混淆程序员的思维,因此是应该避免的。
第三节 类的继承
类的派生(derivation)和继承 (inheritance)是面向对象程序
设计中的一个很重要的概念。通过这种被称作继承的机制,
可以从已有的类派生出新的类,新的类包括了已有类的全部
接口 (interface),而且还可以包括特有的新的接口,从而实
现我们在2。1。3小节中提出的继承结构。本节讲述使用C++编
程语言来实现继承机制的具体的语法细节。
在C++中,若我们从类A派生出新的类B,那么,称类A为类B的
基类 (base class),而类B为类A的派生类 (derived class)
2。3。1 单一派生
所谓的单一派生是指一个派生类只能有一个直接基类。什么
叫直接基类呢?假设我们从类A派生出类B,然后又从类B派生
出类C,那么,类B就是类C的直接基类,与之相对比,我们称
类A为类C的间接基类。这一关系可以用图2。5表示。
…………………………………………………………Page 86……………………………………………………………
图2。 5 单一继承结构
下面的代码实现了上面的结构:
class A
{
// 在此添加类A的实现
// 。。。
};
class B:public A
{
// 在此添加类 B 特有的实现
// 。。。
};
由上面的代码可以看出,类的单一派生使用如下的语法:
class class…name : access…specifier base…class
{
// 派生类的新增成员
};
其中class…name是派生类的类名,access…specifier指定了
基类的继承方式,可以使用的关键字包括public (如前面的
例子所示)、private和protected,它们的作用我们将在下面
作说明,base…class是基类的类名。
同指定类中的成员的访问权限相仿,我们可以使用访问限定
关键字指定派生类对基类成员的访问权限。对于使用不同方
…………………………………………………………Page 87……………………………………………………………
式 (public、private和protected)派生的类,对于基类中以
不同方式 (public、private和protected)定义的成员的访问
限制是不同的。我们在前面讲述类中成员的访问限制时已在
前面的表2。2中已经给出了不同派生方式不区别,为了叙述方
便,这里将与本节有关的内容重新列于表2。6。
表2。 6 不同派生方式得到的派生类对基类成员的访问权限
基类成员所使用的 在派生类中基类的继 派生类对基类成员访问
关键字 承方式 权限
public public 相当于使用了public关
(公有成员) 键字
protected 相当于使用了
protected关键字
private 相当于使用了private
关键字
protected public 相当于使用了
(受保护成员) protected关键字
protected 相当于使用了
protected关键字
private 相当于使用了private
关键字
private public 不可访问
(私有成员)
protected 不可访问
private 不可访问
从表2。6可以看出,无论使用哪种方式生成的派生类,其成员
函数都可以访问基类中除了以private关键进行限定的以外的
其它成员。但是,对于以public方式生成的派生类,在基类
中使用public和protected关键字限定的成员,在派生类中仍
相当于使用了public和protected关键字;对于以protected
方式生成的派生类,在基类中使用public和protected关键字
限定的成员,在派生类中都相当于使用了protected关键字;
而对于以private方式生成的派生类,它们都相当于使用了
private关键字。
n 注意:
…………………………………………………………Page 88……………………………………………………………
n 无论使用的是哪种继承方式,在基类中以private关键字
进行限定的成员在派生类中都是不可以访问的,这和以
protected关键字定义的成员有着很大的 区别,因此,如
果希望成员能够为派生类所访问,而同时又不希望被类
外部的其它函数直接访问,那么应该使用的访问限定符
是protected,而不是private。
2。3。2 多重继承
前面说过,当一个派生类只有一个基类时,我们称这种继承
方式为单一继承。事实上,在C++中,我们还允许一个派生类
具有多于一个的基类,这种派生方式被称为多重继承
(multiple inheritance)。多重继承是单一继承的自然扩
展,两者既有同一性,又有特殊性。图2。6给出了一个多重继
承的示例。在这个示例中,类Software由两个类
OperatingSystem和Application派生,而类puter又由类
Software和另一个类Hardware派生。
实现该示例的C++语言代码如下:
图2。 6 多重继承结构
#include
#include
#include
class OperatingSystem
{
private:
char OsName'80';
…………………………………………………………Page 89……………………………………………………………
public:
const char *GetOsName() const
{
return OsName;
}
OperatingSystem()
{
}
OperatingSystem(char *OsName)
{
strcpy(OperatingSystem::OsName;OsName);
}
};
class Application
{
private:
char *(AppName'80');
char n;
public:
const char *GetAppName(int i) const
{
return AppName'i';
}
int AddAppName(char *AppName)
{
char *NewAppName=new char'80';
…………………………………………………………Page 90……………………………………………………………
strcpy(NewAppName;AppName);
Application::AppName'++n'=NewAppName;
return n;
}
int GetAppCount()
{
return n;
}
Application()
{
n=0;
}
~Application()
{
for (int i=1;i
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!