c++函数为什么要用常量引用类型指针形参 引用形参

图说函数模板右值引用参数(T&&)类型推导规则(C++11)_C++_何问起
您的位置: -
图说函数模板右值引用参数(T&&)类型推导规则(C++11)
规律总结:
只要我们传递一个基本类型是A④的左值,那么,传递后,T的类型就是A&,形参在函数体中的类型就是A&。
只要我们传递一个基本类型是A的右值,那么,传递后,T的类型就是A,形参在函数体中的类型就是A&&。
另外,模板参数类型推导是保留cv限定符(cv-qualifier,const和volatile限定符的统称)的,具体例子见《完美转发和标准库forward函数》。
①这里指形参在函数体中的实际类型
②函数返回的不具名左值引用依旧是左值,例如,上面代码中,Get(3)=5;是可以的。
③具名的右值引用是左值,不具名的右值引用是右值。
④这里,&基本类型是A&意思是:A,A&,A&&及A类型的字面值的基本类型都是A。一、写在前面 & &
& & 作为一个初级而且想成为高级的程序员,对浩瀚的代码世界总是充满的好奇与学习的动力,哈哈,别笑,我是认真的。最近之所以频繁的更新博文,而且知识点相对比较广,但大多也是C++各方面的基础,再加上最近时不时度一些别人在微信公众号上推送的技术文章(如果你也想了解这些技术公众号,也可以关注我,我反正觉得蛮好的,查看这位博友的博文吧),就产生了一些技术联想,恰好又在写代码的过程中用到了或遇到了,索性就把这些都记录下来吧。
二、CPU指令执行局部性原理
& & 一个编写良好的计算机程序代码常常具有良好的局部性,也就是说它们倾向于引用最近引用过的数据项本身,或者引用与最近引用过的数据项在内存空间(关于内存空间的解释请看)中相隔比较近的数据项,这种倾向性称为局部性原理。局部性原理包含两个方面的内容:时间局部性和空间局部性。所谓时间局部性指的是当前被引用的这个变量很可能在不久的将来(即几个机器指令周期之后)在此被引用;而空间局部性指的是CPU很可能在不久的将来引用与当前所引用变量内存空间中相邻的变量。这里举例两个时间局部性和空间局部性的例子,关于时间局部性,比如一个函数定义如下:
void fun(int n)
//数组a[100]是一个全局数组
for(int i=0; i&100; i++)
a[i] = a[i] +
}如果调用函数fun(10),则在for循环内CPU每次都需要重新读取n的值(尽管现在一些编译器会做一定的优化),这就不是一个时间局部性良好的代码,可以修改为如下:
void fun(int n)
//定义局部变量,接管实参n的值,避免CPU每次都重新读取n的值,保证时间局部性
//数组a[100]是一个全局数组
for(int i=0; i&100; i++)
a[i] = a[i] +
对于空间局部性,以访问二维数组为例:void fun_1(int *a[M], int N)
//N为二维数组行数,M为二维数组列数
for(int i=0; i&N; i++)
for(int j=0; j&M; j++)
a[i][j] = a[i][j] + 1;
void fun_2(int *a[M], int N)
//N为二维数组行数,M为二维数组列数
for(int i=0; i&M; i++)
for(int j=0; j&N; j++)
a[i][j] = a[i][j] + 1;
}比较函数fun_1()和fun_2(),可以发现,他们的函数体基本相同,都是包含两个for循环,也就是遍历二维数组的各个元素,但是函数fun_1()比fun_2()的空间局部性要好,为什么呢?因为二维数组在计算机内部是按行存储的。
三、C++中尽量使用const引用形参
& & 为什么呢?我的理解这也是为了保证时间局部性,在上面的例子中,我提到通过定义局部变量,即拷贝实参来保证时间局部性,但是当实参是一个很大的数组(数组无法拷贝)、或者是一个没有定义拷贝构造函数(关于C++拷贝构造函数可以链接)的对象(这时编译器需要决定是否要合成拷贝构造函数,然后再执行相应的拷贝操作),但是拷贝的过程需要更长的时间消耗,本来定义一个局部变量是一种以内存空间换时间的概念,但是现在局部变量的拷贝时间太长,导致内存空间被消耗,同时并没有换取时间的减少,所以是得不偿失的,那怎么办呢?C++提供了const关键字,const关键字的作用很多,这里只解释它作为函数形参的一个作用,主要有两个作用:(1)它告诉编译器,这个参数是一个常量,首先你在函数内部不能改变它;(2)其次,如果在函数内部需要多次引用这个值,CPU不必每次都重新读取,直接使用第一次读取的值(我想应该是存放在寄存器文件中的)。
四、写在后面
& & 以下纯属个人理解,如有错误或不足的地方请批评指正。
& & 现在关于编写高效优质的代码有三种说法,一部分认为需要以内存空间换时间,因为现代计算机的内存速度、大小都有很大的进步,内存空间紧张已不再是问题;另一部分认为需要以时间换空间,因为他们任务现代计算机的计算单元的计算速度已经很快了,不需要考虑计算效率的问题;还有一部分人(那就是我,哈哈),综合前两种情况,在适当情况下,当然可以两种互换啦。
&&相关文章推荐
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:1384次
排名:千里之外
原创:18篇
(7)(6)(1)(1)(3)(1)C++编程中将引用类型作为函数参数的方法指南
投稿:goldensun
字体:[ ] 类型:转载 时间:
这篇文章主要介绍了C++编程中将引用类型作为函数参数的方法指南,是C++入门学习中的基础知识,需要的朋友可以参考下
有了变量名,为什么还需要一个别名呢?C++之所以增加引用类型, 主要是把它作为函数参数,以扩充函数传递数据的功能。
到目前为止我们介绍过函数参数传递的两种情况。
1) 将变量名作为实参和形参
这时传给形参的是变量的值,传递是单向的。如果在执行函数期间形参的值发生变化,并不传回给实参。因为在调用函数时,形参和实参不是同一个存储单元。
【例】要求将变量i和j的值互换。下面的程序无法实现此要求。
#include &iostream&
int main( )
void swap(int,int); //函数声明
int i=3,j=5;
swap(i,j); //调用函数swap
cout&&i&&" "&&j&& //i和j的值未互换
void swap(int a,int b) //企图通过形参a和b的值互换,实现实参i和j的值互换
temp=a; //以下3行用来实现a和b的值互换
运行时输出3 5i和j的值并未互换。
为了解决这个问题,采用传递变量地址的方法。
2) 传递变量的指针
形参是指针变量,实参是一个变量的地址,调用函数时,形参(指针变量)指向实参变量单元。程序见例6.19。
【例】使用指针变量作形参,实现两个变量的值互换。
#include &iostream&
int main( )
void swap(int *,int *);
int i=3,j=5;
swap(&i,&j); //实参是变量的地址
cout&&i&&" "&&j&& //i和j的值已互换
void swap(int *p1,int *p2) //形参是指针变量
temp=*p1; //以下3行用来实现i和j的值互换
形参与实参的结合见图示意。
这种虚实结合的方法仍然是“值传递”方式,只是实参的值是变量的地址而已。通过形参指针变量访问主函数中的变量(i和j),并改变它们的值。这样就能得到正确结果,但是在概念上却是兜了一个圈子,不那么直截了当。
在Pascal语言中有“值形参”和“变量形参”(即var形参),对应两种不同的传递方式,前者采用值传递方式,后者采用地址传递方式。在C语言中,只有“值形参”而无“变量形参”,全部采用值传递方式。C++把引用型变量作为函数形参,就弥补了这个不足。
C++提供了向函数传递数据的第(3)种方法,即传送变量的别名。
【例】利用“引用形参”实现两个变量的值互换。
#include &iostream&
int main( )
void swap(int &,int &);
int i=3,j=5;
swap(i,j);
cout&&"i="&&i&&" "&&"j="&&j&&
void swap(int &a,int &b) //形参是引用类型
输出结果为:
在swap函数的形参表列中声明a和b 是整型变量的引用。
实际上,在虚实结合时是把实参i的地址传到形参a,使形参a的地址取实参i的地址,从而使a和i共享同一单元。同样,将实参j的地址传到形参b,使形参b的地址取实参j的地址,从而使b和j共享同一单元。这就是地址传递方式。为便于理解,可以通俗地说:把变量i的名字传给引用变量a,使a成为i的别名。
请思考:这种传递方式和使用指针变量作形参时有何不同?可以发现:使用引用类型就不必在swap函数中声明形参是指针变量。指针变量要另外开辟内存单元,其内容是地址。而引用变量不是一个独立的变量,不单独占内存单元,在例中引用变量a和b的值的数据类型与实参相同,都是整型。
在main函数中调用swap函数时,实参不必用变量的地址(在变量名的前面加&),而直接用变量名。系统向形参传送的是实参的地址而不是实参的值。
这种传递方式相当于Pascal语言中的“变量形参”,显然,这种用法比使用指针变量简单、直观、方便。使用变量的引用,可以部分代替指针的操作。有些过去只能用指针来处理的问题,现在可以用引用来代替,从而降低了程序设计的难度。
【例】对3个变量按由小到大的顺序排序。
#include &iostream&
int main( )
void sort(int &,int &,int &); //函数声明,形参是引用类型
int a,b,c; //a,b,c是需排序的变量
int a1,b1,c1; //a1,b1,c1最终的值是已排好序的数列
cout&&"Please enter 3 integers:";
cin&&a&&b&&c; //输入a,b,c
a1=a;b1=b;c1=c;
sort(a1,b1,c1); //调用sort函数,以a1,b1,c1为实参
cout&&"sorted order is "&&a1&&" "&&b1&&" "&&c1&& //此时a1,b1,c1已排好序
void sort(int &i,int &j,int &k) //对i,j,k 3个数排序
void change(int &,int &); //函数声明,形参是引用类型
if (i&j) change (i,j); //使i&=j
if (i&k) change (i,k); //使i&=k
if (j&k) change (j,k); //使j&=k
void change (int &x,int &y) //使x和y互换
运行情况如下:
Please enter 3 integers:23 12 -345↙
sorted order is -345 12 23
可以看到:这个程序很容易理解,不易出错。由于在调用sort函数时虚实结合使形参i,j,k成为实参a1,b1,c1的引用,因此通过调用函数sort(a1, b1, c1)既实现了对i,j,k排序,也就同时实现了对a1,b1,c1排序。同样,执行change (i, j)函数,可以实现对实参i和j的互换。
引用不仅可以用于变量,也可以用于对象。例如实参可以是一个对象名,在虚实结合时传递对象的起始地址。这会在以后介绍。
当看到&a这样的形式时,怎样区别是声明引用变量还是取地址的操作呢?当&a的前面有类型符时(如int &a),它必然是对引用的声明;如果前面无类型符(如cout&&&a),则是取变量的地址。
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具C++函数模板非类型参数的使用介绍
用圣才电子书APP或微信扫一扫,在手机上阅读本文,也可分享给你的朋友。
C++编程语言中的模板应用是一个非常重要的应用技术,我们曾经通过多篇文章对此进行过详细的介绍。那么今天我们就先来了解一下有关C++函数模板非类型参数的一些基本概念,方便大家理解。
C++函数模板非类型参数主要用来为函数提供一个运算常量。关于非类型的函数模板参数,书中有下面的例子:
1.&//函数模板定义&
2.&template&typename T, int VAL&
3.&T addValue(T const& x)&
5.&return x + VAL;&
7.&//其他代码&
8.&//函数模板的使用&
9.&std::transform(source.begin(), source.end(), dest.begin(),&
10.&(int(*) (int const&))addValue&int, 5&);
上面的代码中定义了一个函数模板,目的是对传入的参数加上一个指定的int型的5。这样的函数被普遍的使用在对一组数据进行同一处理的场合。例如,12行。这里需要注意的是:一std::transform函数本身就是一个模板函数,它的最后一个参数可以传递一个函数指针。
因此,(int(*) (int const&))addValue&int, 5&其实是一个指向实例化后的addValue&T, int VAL&模板函数的指针。至于这个指针怎么读,还请高手指教。另外需要注意的一点是,std::transform的最后一个参数不一定要是模板函数,任何函数都可以(关于std::transform的正确理解参考下面的评论)。只是模板函数更合适处理多种类型的数据罢了。
C++函数模板非类型参数的限制。
关于非类型模板参数的限制目前记住它可以是常整型(包括枚举类型)和指向外部连接对象的指针就可以可了。由于历史原因,浮点型不能作为非类型模板的参数;而指针和字符串作为非类型模板的参数是有条件的。我想这与变量的作用范围和生命周期有关吧。书中后面会有比较相信的介绍,就等到时候再细看了。
  来源:网络&&&&&&& 作者:不详
小编工资已与此赏挂钩!一赏一分钱!求打赏↓ ↓ ↓
如果你喜欢本文章,请赐赏:
已赐赏的人
我的电子书}

我要回帖

更多关于 函数的形参类型不确定 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信