热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

C++不足之处讨论系列(四)--函数重载

以下文章翻译自IanJoyner所著的《C++??ACritiqueofC++andProgrammingandLanguageTrendsofthe1990s》3E【I

以下文章翻译自Ian Joyner所著的
《C++?? A Critique of C++ and Programming and Language Trends of the 1990s》 3/E【Ian Joyner 1996】
原著版权属于Ian Joyner,征得Ian Joyner本人的同意,我得以将该文翻译成中文。因此,本文的中文版权应该属于我;-)
该文章的英文及中文版本都用于非商业用途,你可以随意地复制和转贴它。不过最好请在转贴时加上前面的这段声明。
如果有人或机构想要出版该文,请最好联系原著版权所有人及我。

另外,该篇文章已经包含在Ian Joyner所写的《Objects Unencapsulated 》一书中(目前已经有了日文的翻译版本),该书的介绍可参见于:
http://www.prenhall.com/allbooks/ptr_0130142697.html
http://efsa.sourceforge.net/cgi-bin/view/Main/ObjectsUnencapsulated
http://www.accu.org/bookreviews/public/reviews/o/o002284.htm

Ian Joyner的联系方式:
 i.joyner@acm.org
我的联系方式:
 cber@email.com.cn

译者前言:
 要想彻底的掌握一种语言,不但需要知道它的长处有哪些,而且需要知道它的不足之处又有哪些。这样我们才能用好这门语言,避免踏入语言中的一些陷阱,更好地利用这门语言来为我们的工作所服务。
 Ian Joyner的这篇文章以及他所著的《Objects Unencapsulated 》一书中,向我们充分的展示了C++的一些不足之处,我们应该充分借鉴于他已经完成的伟大工作,更好的了解C++,从而写出更加安全的C++代码来。

C++的不足之处讨论系列(四)

函数重载

 C++允许在参数类型不同的前提下重载函数。重载的函数与具有多态性的函数(即虚函数)不同处在于:调用正确的被重载函数实体是在编译期间就被决定了的;而对于具有多态性的函数来说,是通过运行期间的动态绑定来调用我们想调用的那个函数实体。多态性是通过重定义(或重写)这种方式达成的。请不要被重载(overloading)和重写(overriding)所迷惑。重载是发生在两个或者是更多的函数具有相同的名字的情况下。区分它们的办法是通过检测它们的参数个数或者类型来实现的。重载与CLOS中的多重分发(multiple dispatching)不同,对于参数的多重分发是在运行期间多态完成的。
 
 【Reade 89】中指出了重载与多态之间的不同。重载意味着在相同的上下文中使用相同的名字代替出不同的函数实体(它们之间具有完全不同的定义和参数类型)。多态则只具有一个定义体,并且所有的类型都是由一种最基本的类型派生出的子类型。C. Strachey指出,多态是一种参数化的多态,而重载则是一种特殊的多态。用以判断不同的重载函数的机制就是函数标示(function signature)。
 
 重载在下面的例子中显得很有用:
 max( int, int )
 max( real, real )
 
 这将确保相对于类型int和real的最佳的max函数实体被调用。但是,面向对象的程序设计为该函数提供了一个变量,对象本身被被当作一个隐藏的参数传递给了函数(在C++中,我们把它称为this)。由于这样,在面向对象的概念中又隐式地包含了一种对等的但却更有更多限制的形式。对于上述讨论的一个简单例子如下:
 int i, j;
 real r, s;
 i.max(j);
 r.max(s);
 
 但如果我们这样写:i.max(r),或是r.max(j),编译器将会告诉我们在这其中存在着类型不匹配的错误。当然,通过重载运算符的操作,这样的行为是可以被更好地表达如下:
 i max j 或者
 r max s
 但是,min和max都是特殊的函数,它们可以接受两个或者更多的同一类型的参数,并且还可以作用在任意长度的数组上。因此,在Eiffel中,对于这种情况最常见的代码形式看起来就像这样:
 il:COMPARABLE_LIST[INTEGER]
 rl:COMPARABLE_LIST[REAL]
 
 i := il.max
 r := rl.max
 
 上面的例子显示,面向对象的编程典范(paradigm),特别是和范型化(genericity)结合在一起时,也可以达到函数重载的效果而不需要C++中的函数重载那样的声明形式。然而是C++使得这种概念更加一般化。C++这样作的好处在于,我们可以通过不止一个的参数来达到重载的目的,而不是仅使用一个隐藏的当前对象作为参数这样的形式。
 
 另外一个我们需要考虑的因素是,决定(resolved)哪个重载函数被调用是在编译阶段完成的事情,但对于重写来说则推后到了运行期间。这样看起来好像重载能够使我们获得更多性能上的好处。然而,在全局分析的过程中编译器可以检测函数min和max是否处在继承的最末端,然后就可以直接的调用它们(如果是的话)。这也就是说,编译器检查到了对象i和r,然后分析对应于它们的max函数,发现在这种情况下没有任何多态性被包含在内,于是就为上面的语句产生了直接调用max的目标代码。与此相反的是,如果对象n被定义为一个NUMBER,NUMBER又提供一个抽象的max函数声明(我们所用的REAL.max和INTERGER.max都是从它继承来的),那么编译器将会为此产生动态绑定的代码。这是因为n既可能是INTEGER,也有可能是REAL。
 
 现在你是不是觉得C++的这种方法(即通过提供不同的参数来实现函数的重载)很有用?不过你还必须明白,面向对象的程序设计对此有着种种的限制,存在着许多的规则。C++是通过指定参数必须与基类相符合的方式实现它的。传入函数中的参数只能是基类,或是基类的派生类。例如:
 A.f( B someB ) {...}
 class B ...;
 class D : public B ...;
 A a;
 D d;
 a.f( d );
 其中d必须与类'B'相符,编译器会检测这些。
 
 通过不同的函数签名(signature)来实现函数重载的另一种可行的方法是,给不同的函数以不同的名字,以此来使得它们的签名不同。我们应该使用名字来作为区分不同实体(entities)的基础。编译器可以交叉检测我们提供的实参是否符合于指定的函数需要的形参。这同时也导致了软件更好的自记录(self-document)。从相似的名字选择出一个给指定的实体通常都不会很容易,但它的好处确实值得我们这样去做。
 
 [Wiener95]中提供了一个例子用以展示重载虚拟函数可能出现的问题:
 class Parent
 {
  public:
   virutal int doIt( int v )
   {
    return v * v;
   }
 };
 
 class Child: public Parent
 {
  public:
   int doIt( int v, int av = 20 )
   {
    return v * av;
   }
 };
 
 int main()
 {
  int i;
  Parent *p = new Child();
  i = p->doIt(3);
  return 0;
 }
 
 当程序执行完后i会等于多少呢?有人可能会认为是60,然而结果却是9。这是因为在Child中doIt的签名与在Parent中的不一致,它并没有重写Parent中的doIt,而仅仅是重载了它,在这种情况下,缺省值没有任何作用。
 
 Java也提供了方法重载,不同的方法可以拥有同样的名字及不同的签名。
 
 在Eiffel中没有引入新的技术,而是使用范型化、继承及重定义等。Eiffel提供了协变式的签名方式,这意味着在子类的函数中不需要完全符合父类中的签名,但是通过Eiffel的强类型检测技术可以使得它们彼此相匹配。

 
推荐阅读
  • C++发布订阅和观察者模式的区别是什么
    本篇内容主要讲解“C++发布订阅和观察者模式的区别是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 本文探讨了C语言中指针的应用与价值,指针在C语言中具有灵活性和可变性,通过指针可以操作系统内存和控制外部I/O端口。文章介绍了指针变量和指针的指向变量的含义和用法,以及判断变量数据类型和指向变量或成员变量的类型的方法。还讨论了指针访问数组元素和下标法数组元素的等价关系,以及指针作为函数参数可以改变主调函数变量的值的特点。此外,文章还提到了指针在动态存储分配、链表创建和相关操作中的应用,以及类成员指针与外部变量的区分方法。通过本文的阐述,读者可以更好地理解和应用C语言中的指针。 ... [详细]
  • 如何用Vscode和 Visual stdudio配置C++环境
    这篇文章主要讲解了“如何用Vscode和Visualstdudio配置C++环境”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深 ... [详细]
  • 一、MyEclipse中的一些常用的快捷键:ctrl+shift+x大写ctrl+shift+y小写alt+内容提示写住方法的时候可以先写main然后按alt+就可以了ctrl+1 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • C# 7.0 新特性:基于Tuple的“多”返回值方法
    本文介绍了C# 7.0中基于Tuple的“多”返回值方法的使用。通过对C# 6.0及更早版本的做法进行回顾,提出了问题:如何使一个方法可返回多个返回值。然后详细介绍了C# 7.0中使用Tuple的写法,并给出了示例代码。最后,总结了该新特性的优点。 ... [详细]
  • Android JSON基础,音视频开发进阶指南目录
    Array里面的对象数据是有序的,json字符串最外层是方括号的,方括号:[]解析jsonArray代码try{json字符串最外层是 ... [详细]
  • 猜字母游戏
    猜字母游戏猜字母游戏——设计数据结构猜字母游戏——设计程序结构猜字母游戏——实现字母生成方法猜字母游戏——实现字母检测方法猜字母游戏——实现主方法1猜字母游戏——设计数据结构1.1 ... [详细]
  • ASP.NET2.0数据教程之十四:使用FormView的模板
    本文介绍了在ASP.NET 2.0中使用FormView控件来实现自定义的显示外观,与GridView和DetailsView不同,FormView使用模板来呈现,可以实现不规则的外观呈现。同时还介绍了TemplateField的用法和FormView与DetailsView的区别。 ... [详细]
  • Threadtowaitinfinitelyinaloopuntilaflagstatechange,thencallfunction.线程在循环中无限期等待,直到标志状 ... [详细]
  • 题目大意:给定数字,将其转化为罗马数字的形式罗马数字其实只有IVXLCDM这几种形式,其余均为组合的,去百度了解一下就ok。所以首先想到的就是,将个、十、百、千位的 ... [详细]
  • 编程题目是:定义一个整数计算类Integer,实现短整数+,-,*,基本算术运算。要求可以进行数据范围检查(-32768~32767,或自行设定),且在数据溢出时显示错误信息并中断程序执行。 ... [详细]
  • Java中处理大数据问题(BigInteger、BigDecimal)
    原文转自:https:blog.csdn.netzhongkeleearticledetails52289163;http:www.cnblogs.c ... [详细]
author-avatar
布瓜Pourqu2502854853
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有