你或许不了解的C++函数调用(1)

这篇博客名字起得可能太自大了,搞得自己像C++大牛一样,其实并非如此。C++有很多隐藏在语法之下的特性,使得用户可以在不是特别了解的情况下简单使用,这是非常好的一件事情。但是有时我们可能会突然间发现一个很有意思的现象,然后去查资料,最终学到了C++的一个特性。所以很可能每个人理解的C++都有很大不同,我只是从自己的角度去跟大家分享而已。

C++的函数调用相比于C的函数调用要复杂很多,这主要是由于函数重载命名空间等特性造成的。

根据Stephan T. Lavavej的介绍,C++编译器在解析一次函数调用的时候,要按照顺序做以下事情(根据具体情况,有些步骤可能会跳过的):

1) 名字查找(Name Lookup)

2) 模板参数类型推导(Template Argument Deduction)

3) 重载决议(Overload Resolution)

4) 访问控制(Access Control)

5) 动态绑定(Dynamic Binding)

 

本篇博客主要跟大家分享下自己对Name lookup的理解。

对于编译器来说,完成一次函数调用之前,必须能够先找到这个函数。在C中这个问题很简单,就是函数调用点向上找函数声明,如果能找到就匹配,如果找不到就报错。在C++中有函数重载(Function Overload)名字空间(Namespace)的概念,使得这个问题变得有些复杂,但非常有意思。

一、从一段程序讲起

 

首先,问大家个问题,在C++程序中,我们经常这样写:

#include <iostream>int main()
{std::cout << "Hello, Core C++!" << std::endl;
}

请问:上面main函数中的语句使用了重载操作符<<,如果用普通函数调用的语法该怎么写?

显然,这个语句一共有两次operator<<函数调用。那么这两个operator<<函数调用是一样的函数吗?如果不是,区别在哪里?

OK,告诉大家答案吧,上面的代码等价于这样写:

#include <iostream>int main()
{operator<<(std::cout, "Hello, Core C++!");std::cout.operator<<(std::endl);
}

大家看出来了吧?第一次operator<<调用的是一个全局函数,而第二次调用的是一个成员函数

如果再深入一些,std::endl到底是个什么东西?直觉上这就是用来换行的,可能就是一个\n。而事实上,std::endl是一个函数。为什么呢?我们先看看VC中std::endl的代码:

template<class _Elem,class _Traits> inlinebasic_ostream<_Elem, _Traits>&__CLRCALL_OR_CDECL endl(basic_ostream<_Elem, _Traits>& _Ostr){    // insert newline and flush stream_Ostr.put(_Ostr.widen('\n'));_Ostr.flush();return (_Ostr);}

std::endl是一个全局函数,接受一个basic_ostream参数_Ostr。函数内部做了两件事情:一、调用_Ostr的put(const char*)成员函数,输出\n;二、调用_Ostr的flush()函数。其中第二步保证了ostream立即刷新,这也就是std::cout<<”\n”和std::cout<<std::endl的区别。也就只有std::endl是个函数才能完成这样的操作。

还是最开始的例子,如果写成这样:

#include <iostream>int main()
{cout << "Hello, Core C++!" << endl;
}

编译器会提示“undeclared identifier”,因为我们没有指定任何namespace,编译器默认到全局命名空间中查找,相当于::cout << "Hello, Core C++!" << ::endl;,而程序中并没有提供的cout和endl,因此找不到。这个大家应该都比较熟悉了。

再问大家一个问题:

operator<<(std::cout, "Hello, Core C++!");

为什么这个语句不写成:

std::operator<<(std::cout, "Hello, Core C++!");

也能通过编译呢?毕竟operator<<是在std名字空间里,全局名字空间里面并没有,为什么没有报错呢?

二、Name Lookup的主要机制

这就要从C++标准中对于名字查找的描述说起了。C++中有三种主要名字查找机制:

a) 隐式名字查找(Unqualified name lookup)

b) 基于参数的名字查找(Argument-dependent name lookup,ADL)

c) 显式名字查找(Qualified name lookup)

显然,如果变量和函数之前不写任何名字空间,就是隐式名字查找,此时编译器只会从当前命名空间和全局命名空间中查找;如果写了名字空间,就是显式名字查找,编译器会忠实地按照指定的命名空间去查找。

最有意思的是基于参数的名字查找,简称ADL,也叫Koenig Lookup,这种名字查找方式是C++大牛Andrew Koenig发明的。具体来说,对于一个函数调用,如果没有显式地写函数的名字空间,编译器会根据函数的参数所在的名字空间里面去查找这个函数。最新的C++标准加强了这个规则,叫Pure ADL,也就是只到参数所在的名字空间里去查找,而不到其它名字空间里查找,这样的好处是防止找到其它名字空间里具有相同签名的函数,导致非常隐蔽的bug。

这就可以理解为什么

operator<<(std::cout, "Hello, Core C++!");

可以正常编译了,因为函数中有std::cout这个参数,所以编译器就会到std名字空间里去查找operator<<这个函数。

这个特点非常重要,否则C++中的操作符重载根本无法做到像现在如此简洁。可以想象下,如果每次都要去指定操作符的命名空间,语法该有多丑!仅仅通过ADL,就可以看出Andrew Koenig对于C++的贡献。

注意

std::cout.operator<<(std::endl);

这个语句不能省略最前面的std::,这是因为C++中类本身也形成了一个名字空间(就是类名),也就是说std::cout.operator<<这个函数的名字空间是std:ostream,而不是std,而std::endl在std名字空间中,ADL是不会向下去查找嵌套的名字空间的的,只会在当前名字空间里去查找。因此最前面的std::不能省略。

三、名字空间污染

对已一开始的例子,可能很多人更喜欢写成:

#include <iostream>
using namespace std;int main()
{cout << "Hello, Core C++!" << endl;
}

这样下面使用任何STL里面的类和算法的时候,都不用加上std::前缀了,这样是方便,但是也是会带来问题的。using namespace std;这个语句将std里面所有的东西(类、算法、对象等等)都引入到我当前的名字空间中,其中很多东西我是暂时使用不到的。如果我自己在当前名字空间中定义了一些和std中同名的东西的话,就会导致一些意想不到的问题:

#include <iostream>
using namespace std;class Polluted {
public:Polluted& operator<<(const char*){return *this;}
};
int main()
{Polluted cout;cout << "Hello, Core C++!\n";
}

上面这个程序,看上去会输入Hello, Core C++!,实际上却什么都没做。因为cout已经不是std::cout了,而是Polluted的一个对象,这个对象恰巧也有一个operator<<(const char*)函数。因为名字空间查找和普通变量的作用域一样,局部名字空间会覆盖全局名字空间和引入的名字空间,所以编译器虽然两个cout都找到,但根据局部优先于全局的规则,选用了main函数中定义的cout,而不是std::cout。

这样的危害在于当程序规模比较大的时候,这样的问题会变得很隐蔽,甚至测试都不一定能测试到,但是却会引发非常奇怪的问题,给调试带来非常大的麻烦。所以using namespace std;尽量少用,最多使用using std::cout,这样就只引入std中的cout,其它东西都没有引入,出问题的概率小些,但问题依旧存在,所以如果可能的话,尽量将std::都加上,保证不出问题。

四、using在STL中的使用

2005年,C++对STL进行了扩充,就是所谓的TR1(Technical Report 1),里面加入了很多实用的库,如shard_ptr、function、bind、regular exprestion等等,它们都位于std::tr1名字空间下。到了C++11,TR1中的很多库得到了升级,正式成为std名字空间中的一员。但是之前很多代码已经用了std::tr1,为了确保已有的代码不被破坏,并且不要重复定义相同的东西。STL采取这样的方式:将原来std::tr1中的定义移到std中,然后在std::tr1中使用using指令将库引入到std::tr1中。如VC中有这样的代码:

namespace tr1 {    // TR1 additions
using _STD allocate_shared;
using _STD bad_weak_ptr;
using _STD const_pointer_cast;
using _STD dynamic_pointer_cast;
using _STD enable_shared_from_this;
using _STD get_deleter;
using _STD make_shared;
using _STD shared_ptr;
using _STD static_pointer_cast;
using _STD swap;
using _STD weak_ptr;
}    // namespace tr1

这样就达到了兼顾新标准和已有代码的目标。

五、名字空间别名

如果我们有一个很深的名字空间,比如A::B::C::D::E,并且经常会用到这里面的类和函数,我们不希望每次都敲这么长的前缀,当然也不希望通过using namespace A::B::C::D::E来污染名字空间,C++提供了名字空间别名的方式来简化使用。比如,我们可以通过

namespace ABCDE = A::B::C::D::E;

产生名字空间别名ABCDE,ABCDE::ClassT就等价于A::B::C::D::E::ClassT。

C++11中,这种方式的别名得到了扩展,不仅仅用于名字空间,可以用于任何别名:

using ABCDE = A::B::C::D::E;
using ABCDE_ClassT = ABCDE::ClassT;

这样的语法基本上可以替代typedef了,而且语法更简洁。

 

OK,关于Name lookup相关的就想到这么多,以后有新的了解再跟大家分享!

转载于:https://www.cnblogs.com/codemood/p/3203537.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/460922.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

有赞下拉菜单html,有赞vant-ui Tabs、List、PullRefresh组件实践

Vant ui Vue.js 部分组件实践功能需求是实现一个移动端的栏目列表切换&#xff0c;于此同时列表需要进行下拉刷新&#xff0c;上拉加载如下图&#xff0c;大概是一个这样的东西看起来是挺简单的&#xff0c;实现起来很方便。没错&#xff0c;我当时也是这么想的&#xff0c;结…

第五季1:AP模式USB-WIFI网卡移植与测试

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 内容总结 &#xff08;1&#xff09;HI3518EWIFI做AP&#xff0c;PC做STA&#xff0c;实现局域网内ortp视频传输。 &#xff08;2&#xff09;HI2518EWIFI做STA&#xff0c;PC做STA&#xff0c;外部…

免费社交类图标集

免费下载 这是一套免费社交类型的图标集&#xff0c;包含35种不同内容的图标。包括当前最热的Instagram&#xff0c;Evernatoe等。和往常推荐的一样&#xff0c;PSD文件可自行调整大小,50*50像素。它们可以被用在商业项目上&#xff0c;如果你也喜欢相关类型的图标&#xff0c;…

如何把图片嵌到html中,自给自足,轻松将图片图片内嵌到HTML

自给自足&#xff0c;轻松将图片图片内嵌到HTML1.为什么要将图片内嵌到HTML&#xff1f;针对于这个问题&#xff0c;我们有如下几种答案1)能减少并发请求&#xff0c;适合且合理的使用&#xff0c;能提高网站的访问速度2)便于分享&#xff0c;完全可以将需要的图片&#xff0c;…

第五季2:STA模式USB-WIFI网卡移植与测试

以下内容源于朱有鹏课程&#xff0c;如有侵权&#xff0c;请告知删除。 参考博客 hisi3518ev200移植MT7601_fsczp的博客-CSDN博客 wpa_supplicant移植与使用_andylauren的博客-CSDN博客 Linux wpa_cli 调试方法_weixin_34168700的博客-CSDN博客 12 海思Hi3518E移植WIFI驱动(ST…

关于ping命令的工作原理

2019独角兽企业重金招聘Python工程师标准>>> 当我们在用ping命令ping地址的时候会向target主机发送一个ICMP协议中的echo包&#xff0c;如果对方living&#xff08;活动状态&#xff09;。 则会返回一个echo包。 TTL的值&#xff1a;这个值对我们有什么用&#xff0…

SPOJ-OPTM Optimal Marks ★★(按位建图 最小割)

【题意】给出一个无向图&#xff0c;每个点有一个标号mark[i]&#xff0c;不同点可能有相同的标号。对于一条边(u, v)&#xff0c;它的权值定义为mark[u] xor mark[v]。现在一些点的标号已定&#xff0c;请决定剩下点的标号&#xff0c;使得总的边权和最小。&#xff08;0 <…

第七季1:分析MP4文件封装格式

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 参考内容 &#xff08;1&#xff09;mp4文件格式解析 - nigaopeng - 博客园&#xff08;推荐&#xff09; &#xff08;2&#xff09;你真的懂 MP4 格式吗&#xff1f; &#xff08;3&#xff09;m…

ESXi主机管理内存资源的方式

因为内存通常是最有限的资源&#xff0c;ESXi采用内存过量配置&#xff08;Memory overcommitment&#xff0c;即配置后的虚拟机内存可能超过物理内存&#xff08;RAM&#xff09;&#xff09;对内存进行管理。使用内存过量配置时&#xff0c;ESXi必须使用技术从一个或多个VM中…

第七季2:移植MP4v2库 与 将H.264码流打包成MP4格式的文件

以下内容源于朱有鹏课程的学习与整理&#xff0c;如有侵权请告知删除。 前言 本文首先移植MP4V2库&#xff0c;然后运行修改后的sample代码以生成MP4文件。 利用MP4v2库&#xff0c;可以将编码方式为H264的视频码流打包、封装为MP4格式的文件。 一、移植MP4v2库 1、下载MP4…

新高考不选物理可以学计算机吗,新高考选科中物理真的那么重要吗?没有选物理是不是完了?...

文/圆梦志愿 伏老师众所周知&#xff0c;选科是我国新高考中最重要的事情之一&#xff0c;且涉及到了多门选考科目。其中&#xff0c;要数物理的话题性相对最强。那么&#xff0c;高考选科是不是物理特别重要&#xff1f;新高考没有选物理是不是完了&#xff1f;一、高考选科是…

参加第三届信息化创新克拉玛依国际学术论坛

据老孙说前二届在克拉玛依的数字油田的会议规格很高&#xff0c;可惜一直没机会参加。2014年9月3日至4日举办第三届&#xff0c;无论如何抓住了这次机会&#xff0c;不过会议的内容有些让我失望&#xff0c;克拉玛依的心思已经放在数字城市上了&#xff0c;数字油田的主题被冲淡…

UNICODE,GBK,UTF-8区别

简 单来说&#xff0c;unicode&#xff0c;gbk和大五码就是编码的值&#xff0c;而utf-8,uft-16之类就是这个值的表现形式&#xff0e;而前面那三种编码是一兼容的&#xff0c;同一个汉字&#xff0c; 那三个码值是完全不一样的&#xff0e;如&#xff02;汉&#xff02;的unco…

学生使用计算机中怎么关机,学会正确开关机初中计算机教案

第1篇&#xff1a;学会正确开关机初中计算机教案学会正确开、关机教案教学目的和要求学会开、关机教学难点&#xff1a;1、了解计算机外设的开、关顺序2、正确学会开、关机教学准备&#xff1a;计算机、网络教学过程()&#xff1a;一、教学导入同学们&#xff0c;在你们面前看到…

Telnet远程登录 与 海思proc文件系统

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c; 如有侵权请告知删除。 内容总结 &#xff08;1&#xff09;添加“ telnet远程登录 ”这种调试方式。 &#xff08;2&#xff09;介绍“ 海思proc文件系统 ”这种调试接口。 一、telnet远程登录 1、telnet远程登录的意…

北邮-上机-提交错误解决及一些经验

2019独角兽企业重金招聘Python工程师标准>>> 1、出现Time Limit Exceed问题 答&#xff1a;三种原因&#xff1a;1、程序死循环或太多的循环&#xff1b;2、对极限状况计算量太大&#xff0c;需要优化程序&#xff0c;如对10000个以上进行排序必须使用快速排序&…

study notes for python

some useful materials Python完全新手教程 http://www.cnblogs.com/taowen/articles/11239.aspx (from taowen, BITer) Note: Part 1 Basic Data Structure List, Dict(dictionary) and Turple are three main data structures in python, which are respond to set,mapping a…

html如何制作滑块,网页制作html5实现滑块功能之type=quot;rangequot;属性-建站-建站教程-建站方法-米云建站 - 米云问答...

html5实现滑块功能之type"range"属性1.html5中添加了关于滑块的标签,其实际是扩展了input标签,type属性值为range。2.感觉挺有意思,就做了一个关于滑块的动画的例子&#xff0c;可以通过开始和结束按钮来控制滑块的滑动和停止&#xff0c;有点类似视频的自动播放和暂…

第七季3:分析“将H.264码流打包成MP4格式的文件”的源码

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 前言 “将H.264MP4码流打包成MP4格式文件”的实验&#xff0c;见博文第七季2&#xff1a;MP4v2库的移植与播放实战。 接下来我们将对相关的源码进行分析。建立SI工程&#xff0c;导入sample_mp4_…

什么代码才是线程安全的

转自&#xff1a;http://www.cnblogs.com/lidabo/archive/2013/04/10/3011518.html 对于多线程编程&#xff0c;很多人概念不清&#xff0c;写代码的时候要么是处处加锁&#xff0c;影响性能不说&#xff0c;还容易莫名其妙的死锁&#xff0c;还有人对多线程敬而远之。所以学习…