C++编程思想05.pdf
![资源得分’ title=](/images/score_1.gif)
![资源得分’ title=](/images/score_1.gif)
![资源得分’ title=](/images/score_1.gif)
![资源得分’ title=](/images/score_1.gif)
![资源得分’ title=](/images/score_05.gif)
《C++编程思想05.pdf》由会员分享,可在线阅读,更多相关《C++编程思想05.pdf(14页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、下载第5章函数重载与缺省参数能使名字方便使用,是任何程序设计语言的一个重要特征。当我们创建一个对象(即变量)时,要为这个存储区取一个名字。一个函数就是一个操作的名字。正是靠系统描述各种各样的名字,我们才能写出易于人们理解和修改的程序。这在很大程度上就像是写散文目的是与读者交流。这里就产生了这样一个问题:如何把人类自然语言的有细微差别的概念映射到编程语言中。通常,自然语言中同一个词可以代表许多种不同的含义,这要依赖上下文来确定。这就是所谓的一词多义该词被重载了。这点非常有用,特别是对于细微的差别。我们可以说“洗衣服,洗汽车”。如果非得说成“洗(洗衣服的洗)衣服,洗(洗汽车的洗)汽车”,那将是很愚
2、蠢的,就好像听话的人对指定的动作毫无辨别能力一样。大多数人类语言都是有冗余的,所以即使漏掉了几个词,我们仍然可以知道话的意思。我们不需要单一的标识而可以从上下文中理解它的含义。然而大多数编程语言要求我们为每个函数设定一个唯一的标识符。如果我们想打印三种不同类型的数据:整型、字符型和实型,我们通常不得不用三个不同的函数名,如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+中,还有另外一个原因需要对函数名重载:构造函数。因为构造函数的名字预先由类的名字
3、确定,所以只能有一个构造函数名。但如果我们想用几种方法来创建一个对象时该怎么办呢?例如创建一个类,它可以用标准的方法初始化,也可以从文件中读取信息来初始化,我们就需要两个构造函数,一个不带参数(缺省构造函数),另一个带一个字符串作为参数,以表示用于初始化对象的文件的名字。所以函数重载的本质就是允许函数同名。在这种情况下,构造函数是以不同的参数类型被调用的。重载不仅对构造函数来说是必须的,对其他函数也提供了很大的方便,包括非成员函数。另外,函数重载意味着,我们有两个库,它们都有一个同名的函数,只要它们的参数不同就不会发生冲突。我们将在这一章中详细讨论这些问题。这一章的主题就是方便地使用函数名。函
4、数重载允许多个函数同名,但还有另一种方法使函数调用更方便。如果我们想以不同的方法调用同一函数,该怎么办呢?当函数有一个长长的参数列表,而大多数参数每次调用都一样时,书写这样的函数调用会使人厌烦,程序可读性也差。C+中有一个很通用的作法叫缺省参数。缺省参数就是在用户调用一个函数时没有指定参数值而由编译器插入参数值的参数。这样 f(“h e l l o”),f(“h i”,1)和f(“h o w d y”,2,c)可以用来调用同一函数。它们也可能是调用三个已重载的函数,但当参数列表相同时,我们通常希望调用同一函数来完成相同的操作。函数重载和缺省参数实际上并不复杂。当我们学习完本章的时候,我们就会明
5、白什么时候用到它们,以及编译、连接时它们是怎样实现的。5.1 范围分解在第2章中我们介绍了名字范围分解的概念(有时我们用“修饰”这个更通用的术语)。在下面的代码中:void f();class x void f();类x内的函数f()不会与全局的f()发生冲突,编译器用不同的内部名 f()(全局)和x:f()(成员函数)来区分两个函数。在第2章中,我们建议在函数名前加类名的方法来命名函数,所以编译器使用的内部名字可能就是 _ f和_ x _ f。函数名不仅与类名关系密切,而且还跟其他因素有关。为什么要这样呢?假设我们重载了两个函数名:void print(char);void print(fl
6、oat);无论这两个函数是某个类的成员函数还是全局函数都无关紧要。如果编译器只使用函数名字的范围,编译器并不能产生单一的内部标识符,这两种情况下都得用 _ p r i n t结尾。重载函数虽然可以让我们有同名的函数,但这些函数的参数列表应该不一样。所以,为了让重载函数正确工作,编译器要用函数名来区分参数类型名。上面的两个在全局范围定义的函数,可能会产生类似于_ p r i n t _ c h a r和_ p r i n t _ f l o a t的内部名。因为,为这样的名字分解规定一个统一的标准毫无意义,所以不同的编译器可能会产生不同的内部名(让编译器产生一个汇编语言代码后我们就可以看到这个内
7、部名是个什么样子了)。当然,如果我们想为特定的编译器和连接器购买编译过的库的话,这就会引起错误。另外,编译器在用不同的方式来产生代码时也可能出现这样的问题。有关函数重载我们就讲到这里,我们可以对不同的函数用同样的名字,只要函数的参数不同。编译器会通过分解这些名字、范围和参数来产生内部名以供连接器使用。5.1.1 用返回值重载读了上面的介绍,我们自然会问:“为什么只能通过范围和参数来重载,为什么不能通过返回值呢?”乍一听,似乎完全可行,同样将返回值分解为内部函数名,然后我们就可以用返回值重载了:void f();int f();当编译器能从上下文中唯一确定函数的意思时,如int x=f();这当
8、然没有问题。然而,在C中,我们总是可以调用一个函数但忽略它的返回值,在这种情况下,编译器如何知道调用哪个函数呢?更糟的是,读者怎么知道哪个函数会被调用呢?仅仅靠返回值来重载函数实在过于微妙了,所以在C+中禁止这样做。5.1.2 安全类型连接对名字的范围分解还可以带来一个额外的好处。这就是,在 C中,如果用户错误地声明了一个函数,或者更糟糕地,一个函数还没声明就调用了,而编译器则按函数被调用的方式去推断函数的声明。这是一个特别严重的问题。有时这种函数是正确的,但如果不正确,就会成为一个很难发现的错误。在C+中,所有的函数在被使用前都必须事先声明,出现上述情况的机会大大减少了。编译器不会自动为我们
9、添加函数声明,所以我们应该包含一个合适的头文件。然而,假如由于某种原因我们还是错误地声明了一个函数,可能是通过自己手工声明,或包含了一个错误的头文件(也许是一个过期的版本),名称分解会给我们提供一个安全网,也就是人们常说的安全连接。70C+编程思想下载请看下面的几个例子。在第一个文件中,函数定义是:/:DEF.CPP-Function definitionvoid f(int)在第二个文件中,函数没有声明就调用了。/:USE.CPP-Function misdeclarationvoid f(char);main()/!f(1);/Causes a linker error即使我们知道函数实际
10、上应该是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 重载的例子现在我们回过头来看看前面的例子,这里我们用重载函
11、数来改写。如前所述,重载的一个很重要的应用是构造函数。我们可以在下面的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下载72C+编程思想下载当我们用第一个构造函数时,没有内存分配给 s t o r a g e,内存是在第一次调用a d d()来增加一个对象时分配的,另外,执行a d d()时,当前的内存块不够用时也会分配内存。下面的测试程序说明了
12、这点,它检查第一个构造函数。第5章 函数重载与缺省参数73下载我们可以修改这些代码,增加其他参数来调用第二个构造函数。这样我们可以选择 s t a s h的初始大小。5.3 缺省参数比较上面两个s t a s h()构造函数,它们似乎并没有多大不同,对不对?事实上,第一个构造函数只不过是第二个的一个特例它的初始大小为零。在这种情况下去创建和管理同一函数的两个不同版本实在是浪费精力。C+中的缺省参数提供了一个补救的方法。缺省参数是在函数声明时就已给定的一个值,如果我们在调用函数时没有指定这一参数的值,编译器就会自动地插上这个值。在 s t a s h的例子中,我们可以把:stash(int Si
13、ze);/zero quantitystash(int Size,int Quantity);用一个函数声明来代替stash(int Size,int Quantity=0);这样,s t a s h(i n t)定义就简化掉了所需要的是一个单一的s t a s h(i n t,i n t)定义。现在这两个对象的定义stash A(100),B(100,0);将会产生完全相同的结果。它们将调用同一个构造函数,但对于 A,它的第二个参数是由编译器在看到第一个参数是整型而且没有第二个参数时自动加上去的。编译器能看到缺省参数,所以它知道应该允许这样的调用,就好像它提供第二个参数一样,而这第二个参数值
14、就是我们已经告诉编译器的缺省参数。缺省参数同函数重载一样,给程序员提供了很多方便,它们都使我们可以在不同的场合使用同一名字。不同之处是,当我们不想亲手提供这些值时,由编译器提供一个缺省参数。上面的那个例子就是用缺省参数代替函数重载的一个很好的例子。用函数重载我们得把一个几乎同样含义、同样操作的函数写两遍甚至更多。当然,如果函数之间的行为差异较大,用缺省参数就不合适了。在使用缺省参数时必须记住两条规则。第一,只有参数列表的后部参数才可是缺省的,也就是说,我们不可以在一个缺省参数后面又跟一个非缺省的参数。第二,一旦我们开始使用缺省参数,那么这个参数后面的所有参数都必须是缺省的。(这可以从第一条中导
15、出。)缺省参数只能放在函数声明中,通常在一个头文件中。编译器必须在使用该函数之前知道缺省值。有时人们为了阅读方便在函数定义处放上一些缺省的注释值。如:void fn(int x /*=0*/)/.缺省参数可以让声明的参数没有标识符,这看上去很有趣。我们可以这样声明:void f(int X,int=0,float=1.1);在C+中,在函数定义时,我们并不一定需要标识符,像:void f(int X,int,float f)/*.*/在函数体中,x和f可以被引用,但中间的这个参数值则不行,因为它没有名字。这种调用还必须用一个占位符(p l a c e h o l d e r),有f(1)或f(
16、1,2,3.0)。这种语法允许我们把一个参数当作占位符而不去用它。其目的在于我们以后可以修改函数定义而不需要修改所有的函数调用。当然,用一个有名字的参数也能达到同样的目的,但如果我们定义的这个参数在函数体内没有使用,74C+编程思想下载多数编译器会给出一条警告信息,并认为我们犯了一个逻辑错误。用这种没有名字的参数就可以防止这种警告产生。更重要的是,如果我们开始用了一个函数参数,而后来发现不需要用它,我们可以高效地将它去掉而不会产生警告错误,而且不需要改动那些调用该函数以前版本的程序代码。位向量类这里我们进一步看一个操作符重载和缺省参数的例子。考虑一个高效存储真假标志集合的问题。如果我们有一批数
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C+ 编程 思想 05
![提示](https://www.taowenge.com/images/bang_tan.gif)
限制150内