没有合适的资源?快使用搜索试试~ 我知道了~
函数参数缺省与重载,命名和缺省规则
需积分: 1 8 下载量 35 浏览量
2010-05-10
19:19:37
上传
评论
收藏 483KB PDF 举报
温馨提示
试读
14页
经常看到函数定义时有好多参数,但实际调用时却只用到其中几个参数,研究老半天终于明白了
资源推荐
资源详情
资源评论
下载
第5章 函数重载与缺省参数
能使名字方便使用,是任何程序设计语言的一个重要特征。
当我们创建一个对象(即变量)时,要为这个存储区取一个名字。一个函数就是一个操作
的名字。正是靠系统描述各种各样的名字,我们才能写出易于人们理解和修改的程序。这在很
大程度上就像是写散文——目的是与读者交流。这里就产生了这样一个问题:如何把人类自然
语言的有细微差别的概念映射到编程语言中。通常,自然语言中同一个词可以代表许多种不同
的含义,这要依赖上下文来确定。这就是所谓的一词多义——该词被重载了。这点非常有用,
特别是对于细微的差别。我们可以说“洗衣服,洗汽车”。如果非得说成“洗(洗衣服的洗)
衣服,洗(洗汽车的洗)汽车”,那将是很愚蠢的,就好像听话的人对指定的动作毫无辨别能
力一样。大多数人类语言都是有冗余的,所以即使漏掉了几个词,我们仍然可以知道话的意思。
我们不需要单一的标识
—
而可以从上下文中理解它的含义。
然而大多数编程语言要求我们为每个函数设定一个唯一的标识符。如果我们想打印三种不
同类型的数据:整型、字符型和实型,我们通常不得不用三个不同的函数名,如 p r i n t _ i n t ( ) 、
p r i n t _ c h a r ( )和p r i n t _ f l o a t ( ) ,这些既增加了我们的编程工作量,也给读者理解程序增加了困难。
在C + +中,还有另外一个原因需要对函数名重载:构造函数。因为构造函数的名字预先由
类的名字确定,所以只能有一个构造函数名。但如果我们想用几种方法来创建一个对象时该怎
么办呢?例如创建一个类,它可以用标准的方法初始化,也可以从文件中读取信息来初始化,
我们就需要两个构造函数,一个不带参数(缺省构造函数),另一个带一个字符串作为参数,
以表示用于初始化对象的文件的名字。所以函数重载的本质就是允许函数同名。在这种情况下,
构造函数是以不同的参数类型被调用的。
重载不仅对构造函数来说是必须的,对其他函数也提供了很大的方便,包括非成员函数。
另外,函数重载意味着,我们有两个库,它们都有一个同名的函数,只要它们的参数不同就不
会发生冲突。我们将在这一章中详细讨论这些问题。
这一章的主题就是方便地使用函数名。函数重载允许多个函数同名,但还有另一种方法使
函数调用更方便。如果我们想以不同的方法调用同一函数,该怎么办呢?当函数有一个长长的
参数列表,而大多数参数每次调用都一样时,书写这样的函数调用会使人厌烦,程序可读性也
差。C + +中有一个很通用的作法叫缺省参数。缺省参数就是在用户调用一个函数时没有指定参
数值而由编译器插入参数值的参数。这样 f ( “h e l l o”) , f ( “h i”, 1 ) 和f ( “h o w d y”, 2 , ‘c’)可以
用来调用同一函数。它们也可能是调用三个已重载的函数,但当参数列表相同时,我们通常希
望调用同一函数来完成相同的操作。
函数重载和缺省参数实际上并不复杂。当我们学习完本章的时候,我们就会明白什么时候
用到它们,以及编译、连接时它们是怎样实现的。
5.1 范围分解
在第2章中我们介绍了名字范围分解的概念(有时我们用“修饰”这个更通用的术语)。在
下面的代码中:
void f();
class x {void f();};
类x内的函数f()不会与全局的 f()发生冲突,编译器用不同的内部名 f()(全局)和x : : f ( )
(成员函数)来区分两个函数。在第 2章中,我们建议在函数名前加类名的方法来命名函数,所
以编译器使用的内部名字可能就是 _ f 和_ x _ f 。函数名不仅与类名关系密切,而且还跟其他因素
有关。
为什么要这样呢?假设我们重载了两个函数名:
void print(char);
void print(float);
无论这两个函数是某个类的成员函数还是全局函数都无关紧要。如果编译器只使用函数名字的
范围,编译器并不能产生单一的内部标识符,这两种情况下都得用 _ p r i n t结尾。重载函数虽然
可以让我们有同名的函数,但这些函数的参数列表应该不一样。所以,为了让重载函数正确工
作,编译器要用函数名来区分参数类型名。上面的两个在全局范围定义的函数,可能会产生类
似于_ p r i n t _ c h a r 和_ p r i n t _ f l o a t的内部名。因为,为这样的名字分解规定一个统一的标准毫无意
义,所以不同的编译器可能会产生不同的内部名(让编译器产生一个汇编语言代码后我们就可
以看到这个内部名是个什么样子了)。当然,如果我们想为特定的编译器和连接器购买编译过
的库的话,这就会引起错误。另外,编译器在用不同的方式来产生代码时也可能出现这样的问
题。
有关函数重载我们就讲到这里,我们可以对不同的函数用同样的名字,只要函数的参数不
同。编译器会通过分解这些名字、范围和参数来产生内部名以供连接器使用。
5.1.1 用返回值重载
读了上面的介绍,我们自然会问:“为什么只能通过范围和参数来重载,为什么不能通过
返回值呢?”乍一听,似乎完全可行,同样将返回值分解为内部函数名,然后我们就可以用返
回值重载了:
void f();
int f();
当编译器能从上下文中唯一确定函数的意思时,如 int x = f();这当然没有问题。然而,在C
中,我们总是可以调用一个函数但忽略它的返回值,在这种情况下,编译器如何知道调用哪个
函数呢?更糟的是,读者怎么知道哪个函数会被调用呢?仅仅靠返回值来重载函数实在过于微
妙了,所以在C + +中禁止这样做。
5.1.2 安全类型连接
对名字的范围分解还可以带来一个额外的好处。这就是,在 C中,如果用户错误地声明了
一个函数,或者更糟糕地,一个函数还没声明就调用了,而编译器则按函数被调用的方式去推
断函数的声明。这是一个特别严重的问题。有时这种函数是正确的,但如果不正确,就会成为
一个很难发现的错误。
在C + + 中,所有的函数在被使用前都必须事先声明,出现上述情况的机会大大减少了。编译
器不会自动为我们添加函数声明,所以我们应该包含一个合适的头文件。然而,假如由于某种
原因我们还是错误地声明了一个函数,可能是通过自己手工声明,或包含了一个错误的头文件
(也许是一个过期的版本),名称分解会给我们提供一个安全网,也就是人们常说的安全连接。
70 C + +编程思想
下载
请看下面的几个例子。在第一个文件中,函数定义是:
//: DEF.CPP -- Function definition
void f(int) {}
在第二个文件中,函数没有声明就调用了。
//: USE.CPP -- Function misdeclaration
void f(char);
main() {
//! f(1); //Causes a linker error
}
即使我们知道函数实际上应该是f ( i n t ) ,但编译器并不知道,因为它被告知 (通过一个明确的
声明) 这个函数是f ( c h a r )。因此编译是成功的,在C中,连接也能成功,但在C + + 中却不行。因
为编译器会分解这些名字,这个函数的定义变成了诸如 f _ i n t之类的名字,而使用的函数则是
f _ c h a r。当连接器试图引用f _ c h a r时,它只能找到f _ i n t,所以它就会报告一条出错信息。这就是
安全连接。虽然这种问题并不经常出现,但一旦出现就很难发现,尤其是在一个大项目中。这
是利用C + +编译器查找C语言程序中很隐蔽的错误的一个例子。
5.2 重载的例子
现在我们回过头来看看前面的例子,这里我们用重载函数来改写。如前所述,重载的一个
很重要的应用是构造函数。我们可以在下面的s t a s h 类中看到这点。
s t a s h ( )的第一个构造函数与前面一样,但第二个带了一个 Q u a n t i t y参数来指明分配内存的
初始大小。在这个定义中,我们可以看到q u a n t i t y 的内部值与s t o r a g e指针一起被置零。
第5章 函数重载与缺省参数 71
下载
剩余13页未读,继续阅读
资源评论
kingrain213
- 粉丝: 77
- 资源: 19
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功