当形参是const时,必须要注意关于顶层const的讨论。如前所述,顶层const的作用于对象本身:
const int ci=42; //不能改变ci,const是顶层的
int i=ci; //正确:当拷贝ci时,忽略了它的顶层const
int *const p=&i; //const是顶层的,不能给p赋值
*p=0; //正确:通过p改变对象的内容是允许的,现在i变成了0
和其他初始化过程一样,当用实参初始化形参时会忽略掉顶层const。换句话说,形参的顶层const被忽略掉了。当形参有顶层const时,传给它的常量对象或者非常量对象都是可以的:
void fcn(const int i){ /*fcn能够读取i,但是不能向i写值*/}
调用fcn函数时,既可以传入const int也可以传入int。忽略掉形参的顶层const可以产生意想不到的结果:
void fcn(const int i) {/*fcn能够读取i,但是不能向i写值*/}
void fcn(int i) {/*....*/}//错误:重复定义了fdn(int)
在C++语言中,允许我们定义若干具有相同名字的函数,不过前提是不同函数的形参列表应该有明显的区别。因为顶层const被忽略了,所以在上面的代码中传入两个fcn函数的参数可以完全一样。因此第二个fcn是错误的,尽管形式上由差异,但实际上它的形参和第一个fcn的形参没什么不同。
指针或引用形参与const
形参的初始化方式和变量的初始化方式是一样的,所以回顾通用的初始化规则有助于理解下面的知识。我们可以使用非常量初始化一个底层的const对象,但是反过来不行;同时一个普通的引用必须用同类型的对象初始化。
int i=42;
const int *cp=&i; //正确:但是cp不能改变i
const int &r=i; //正确:但是r不能改变i
const int &r2=42; //正确
int *p=cp; //错误:p的类型和cp的类型不匹配
int &r3=r; //错误:r3的类型和r的类型不匹配
int &r4=42; //错误:不能用字面值初始化一个非常量引用
将同样的初始化规则应用到参数传递上可得如下形式:
int i=0;
const int ci=i;
string::size_type ctr=0;
void reset(int &i);
reset(&i); //调用形参类型是int *的reset函数
reset(&ci); //错误:不能用指向const int对象的指针初始化int *
reset(i); //调用参数类型是int&的reset函数
reset(ci); //错误:不能把普通引用绑定到const对象ci上
reset(42); //错误:不能把普通引用绑定到字面值上
reset(ctr); //错误:类型不匹配,ctr是无符号类型
//find_char的第一个形参是对常量的引用
find_char("hello world",'o',ctr);//可以绑定到字面值常量上
要想调用引用版本的reset,只能使用int类型的对象,而不能使用字面值、求值结果为int的表达式、需要转换的对象或者const int类型的对象。类似的,要想调用指针版本的reset只能使用int*。
另一方面,我们能传递一个字符串字面值作为find_char的第一个实参,这是因为改函数的引用形参是常量引用,而C++允许我们用字面值初始化常量引用。
尽量使用常量引用
把函数不会改变的形参定义成(普通的)引用是一种比较常见的错误,这么做带来给函数的调用者一种误导,即函数可以修改它的实参的值。此外,使用引用而非常量引用也会极大地限制函数所能接受的实参类型。就像刚刚看到的,我们不能把const对象、字面值或者需要类型转换的对象传递给普通的引用形参。
关于函数传参的其他问题(const形参实参/可变形参)
const 形参和实参 当形参是 const 变量时,实参是 const 或者不是 const 变量都可以. 实参初始化形参时会忽略掉顶层 const: void gel(const int a){ ...
C语言之形参和实参
一 形参与实参 1).定义 形参:形式参数. 就是定义函数的时候,写在函数名小括号后面的参数叫形参 实参:实际参数. 就是调用函数的时候,调用函数时写在函数名小括号后面的参数就叫实参 2).注意项 a ...
const形参与非const形参
在程序设计中我们会经常调用函数,调用函数就会涉及参数的问题,那么在形参列表中const形参与非const形参对传递过来的实参有什么要求呢? 先来看一个简单的例子: #include
c++形参改变实参(对指针的理解
这几天搞逻辑比较晕,居然把指针的概念都混淆了. eg:int *p;//当然不对指针初始化在有些编译器是通不过编译的,比如VS(尤其是选中了SDL) 指针p是一个对象,定义开始没有分配了内存空间,只是 ...
C#形参,实参,值传递参数,引用传递参数,输出参数,参数数组的学习
1)形参 形参顾名思义就是形式上的参数,不是实际的参数,它代替实际传入方法的值.在方法体代码中代表了值本身参与运算.形参定义于参数中,它不同于方法体内局部变量,因为是一个变量,在它的作用域内不允许存在 ...
c++形参和实参同名时,如何单步执行观察形参的变化。
c++形参和实参同名时,如何单步执行观察形参的变化? 方法:当程序运行到函数中时,添加变量观察即可.
关于Java中形参与实参的理解
今天阅读了一个写的非常棒的博文,通过此博文再次复习了Java中参数传递的知识(即值传递与引用传递的区别).参考网站http://www.cnblogs.com/binyue/p/3862276.htm ...
JavaScript中函数的形参和实参的实现原理剖析
我们都知道JS里面参数的传递是可以不一样的,比如我们有一个函数:
[C++程序设计]有关形参与实参,及返回值说明
有关形参与实参的说明:(1) 在定义函数时指定的形参,在未出现函数调用 时,它们并不占内存中的存储单元,因此称它们是 形式参数或虚拟参数,表示它们并不是实际存在的数据,只有在发生函数调用时,函数max ...
随机推荐
【Linux学习】Vi 操作命令集合
Vi 操作命令集合 进入vi的命令 vi filename :打开或新建文件,并将光标置于第一行首 vi +n filename :打开文件,并将光标置于第n行首 vi + filename :打开文 ...
sql server T-SQL 基础
SQL语言按照用途可以分为如下3类: ①DDL(Data Definition Language) 数据定义语言: 定义修改和删除数据库.表.索引和视图等 ②DML(Data Manipulatio ...
Deferred和Promise之间有什么区别呢?
一个promise就是一个由异步函数返回的对象. deferred对象就是jQuery的回调函数解决方案. 总结 jQuery 的ajax 就是返回一个promise 对象,里面含有done(), f ...
简洁之美 -约瑟夫环的python 解法
问题描述: 约瑟夫环问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围.从编号为k的人开始报数,数到k的那个人出列:他的下一个人又从1开始报数,数到k的那个人又出列:依此规律重复下 ...
UGUI实现NGUI的UIEventListener功能
在unity中处理UI事件时,习惯了使用NGUI的UIEventListener,虽然UGUI也有AddListener,但是一个组件只能对应一个函数,不能在一个函数中同时处理多个事件,显得有些麻烦 ...
gitlab中批量删除本地以及远程tag的操作
git 批量删除标签# 删除所有远程标签git show-ref --tag | awk '{print ":" $2}' | xargs git push origin # 删除 ...
省钱版----查找 IoT 设备TTL线序__未完待续
作者:仙果 原文来自:省钱版—-查找 IoT 设备TTL线序 省钱版----查找 IoT 设备TTL线序__未完待续 缘由 在IoT固件调试分析的过程中,建议首先在IoT设备的板子上焊接调试线,这是能 ...
前端Js框架 UI框架汇总 特性 适用范围 选择
身为一个资深后端工程师,面对层出不穷的前端框架,总让人眼花缭乱,做一个综合解析贴,从全局着眼,让我们明白各种前端框架的应用范围,为如何选择前端框架,从不同的维度提供一些线索,做为一个长期优化贴,欢迎指 ...
Java NIO Test Case
package org.zwl.test.nio; import java.io.IOException; import java.net.InetSocketAddress; import java ...