注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

C++中引用说明及重载  

2013-01-19 13:35:20|  分类: C/C++基础 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
一、引用的意义
在函数的传递过程中,个人还是比较偏向于使用引用而不是指针,大致想来,对指针的使用总是觉得它可能为空,也可能是一个非法地址,而对于引用来说,强制要求访问者必须传递一个合法的对象地址。从本质上看,引用是对很多脚本语言和工具中都存在的别名机制(alias)的模拟,例如在sql语句中可能通过 select  X  as Y来对一个数据表中的列进行重命名,这种情况一般是为了将一个通用的表记录项转换为一个订制的内容,别名对于维护来说更加的有意义,或者说更加的程序员友好(而不是机器友好),通常所说的将一个长的名字重命名为一个短的名字也是可能的,但是通常都存在在脚本语言中,例如bash中的alias功能。
在C++中,引用更多的时候是用来进行函数参数的传递,相对于指针来说,引用的传递的操作更少,对于指针来说,通常都需要对一个变量进行取地址,然后传递给函数,而对于引用就可以免除这个操作,当然如果变量保存的本身就是地址,那么就另当别论了。现在想想,C++通常运行在系统配置比较高的主机中,通常为服务器,至少是PC级别的,另一种是对于大规模协调开发的环境中,这种情况下一般软件规模比较大,使用C++可以便于使用功能的基类,使用相同的原语,例如MFC框架以及QT的实现;而且C++提供了更加精细的访问控制和命名空间限制。
二、C++引用类型说明
多态是C++的一个基础,也就是一个基类指针可以表现出不同的行为,而将一个派生类指针向一个基类的转换可以认为是C++的内置转换,这一点在《ANSI C++ reference》中有说明
4.12 Base class conversion [conv.class]
1 An rvalue of type “cv D,” where D is a class type, can be converted to an rvalue of type “cv B,” where B is a base class (10) of D.……The result of the conversion is the value of the base class subobject of the derived class object.
也就是说一个派生类可以被直接转换为一个基类,同样滴,如果一个函数的参数列表中是一个基类的引用,它同样应该可以被自动转换为一个基类的引用,关于这一点在引用的说明中
8.5.3 References
4 Given types “cv1 T1” and “cv2 T2,” “cv1 T1” is referencerelated to “cv2 T2” if T1 is the same type as T2, or T1 is a base class of T2. “cv1 T1” is referencecompatible with “cv2 T2” if T1 is referencerelated to T2 and cv1 is the same cvqualification as, or greater cvqualification than, cv2. For purposes of overload resolution, cases for which cv1 is greater cvqualification than cv2 are identified as referencecompatible with added qualification (see 13.3.3.2). In all cases where the referencerelated or referencecompatible relationship of two types is used to establish the validity of a reference binding, and T1 is a base class of T2, a program that necessitates such a binding is illformed if T1 is an inaccessible (11) or ambiguous (10.2) base class of T2.
也就是一个基类的引用可以传入一个派生类对象。
三、测试代码
[root@Harry autocast]# cat D2B.cpp
struct Base
{};
struct Derive : public Base
{};
int overload(  Base & bbase)
{
    return 0;
}
int main()
{
    return overload(Derive());
}
[root@Harry autocast]# gcc -c   D2B.cpp
D2B.cpp: In function ‘int main()’:
D2B.cpp:11: error: invalid initialization of non-const reference of type ‘Base&’ from a temporary of type ‘Derive’
D2B.cpp:5: error: in passing argument 1 of ‘int overload(Base&)’

可以看到此处有一个编译错误。
四、C++关于函数重载识别说明
13.3.3.1.4 Reference binding
2 A standard conversion sequence cannot be formed if it requires binding a reference to nonconst to an rvalue (except when binding an implicit object parameter; see the special rules for that case in 13.3.1). [Note: this means, for example, that a candidate function cannot be a viable function if it has a nonconst reference parameter (other than the implicit object parameter) and the corresponding argument is a temporary or would require one to be created to initialize the reference (see 8.5.3). ]
这里明确说明了如果传入参数是一个右值(即该值不能出现在等号的左边,或者通俗的说,我们不能通过它来进行修改,最简单的例子就是常量只能作为右值,数组名只能作为右值),而函数的定义是一个非常量的类型引用,那么标准的从Derive到Base类在这里行不通,否则的话就可以通过这种转换来讲一个右值转换为左值。
再看一下gcc代码中对于这提示的位置
initialize_reference (tree type, tree expr, tree decl, tree *cleanup)
{
…… if (!conv || conv->bad_p)
    {
      if (!(TYPE_QUALS (TREE_TYPE (type)) & TYPE_QUAL_CONST)
      && !real_lvalue_p (expr))
    error ("invalid initialization of non-const reference of "
           "type %qT from a temporary of type %qT",
           type, TREE_TYPE (expr));
      else
    error ("invalid initialization of reference of type "
           "%qT from expression of type %qT", type,
           TREE_TYPE (expr));
      return error_mark_node;
    }
……
}

/* Returns the kind of lvalue that REF is, in the sense of
   [basic.lval].  This function should really be named lvalue_p; it
   computes the C++ definition of lvalue.  */

cp_lvalue_kind
real_lvalue_p (tree ref)
{
  return lvalue_p_1 (ref,
             /*treat_class_rvalues_as_lvalues=*/0);
}
C++手册中对于该项的说明
3.10 Lvalues and rvalues
……
5 Constructor invocations and calls to functions that do not return references are always rvalues. User defined operators are functions, and whether such operators expect or yield lvalues is determined by their type
构造函数调用和那些返回值不是引用类型的函数调用始终为右值,这里也即是说在例子中Derive()的返回值始终是一个右值。在函数参数的引用中,如果加上const 引用,那么一个右值就可以被传递进来,因为右值的本身意义就在于不能通过它来进行修改操作,而const限制可以从语义上同样满足这个条件。对于该问题,在引用上添加一个const限制即可。
五、为什么需要这种方法
一般来说是为了传递给函数一个策略(回调),可以认为是STL库中的functor,但是使用模板又感觉没有必要,或者这种引用是为了利用函数重载来实现不同的功能。
  评论这张
 
阅读(837)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017