形参与实参
形参是在定义函数名和函数体的时候使用的参数,目的是用来接收调函数时传递的参数。
实参是在调用时传递给函数的参数,即传递给被调用函数的值。实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值,以便把这些值传送给形参。
形参初始化
-
形参初始化的机理与变量初始化一样。
-
形参的类型决定了形参和实参交互的方式。
如果形参是 引用 类型,它将绑定到对应的实参上;否则,将实参的值拷贝后赋给形参。 -
形参若是引用类型,也就是 引用形参 是 它对应的实参的别名。
否则,在实参的值拷贝给形参后,形参 和 实参分别是两个独立的对象。 -
而对于指针形参,与拷贝赋值同理,但是可以通过形参指针改变对象的值。(对象不是 const 或者指针不是 const int*)
传引用参数
即将 实参的对象绑定个形参,和 普通引用没有区别。
但是 传递引用参数 可以避免拷贝。 拷贝大的类类型或者容器对象是比较低效的,或者有些根本不支持拷贝。
使用 引用参数 返回额外的信息。
可知,一个函数只能返回一个值,但是利用 引用形参,可以达到 等效的 返回多个值的结果。
如:求一个字符串中,a 和 b 出现的次数。便可以这样:
int cal(const string &str,int &cnta) {
int cntb = 0;
for(auto it : str) {
if(it == 'a') ++ cnta;
else if(it == 'b') ++ cntb;
}
return cntb;
}
const 形参和实参
对于这类,需要注意顶层 const。 顶层 const 作用于对象本身。
用实参初始化形参时会忽略掉 顶层const,即形参的顶层 const 被忽略掉了。
当形参有顶层 const 时,传给他常量对象或者非常量对象都可以。
即:
void fcn(const int i) { /* fcn 能够读取 i,但是不能改变 i */ }
调用 fcn 函数时,可以传入 const int , 也可以传入 int
!!注意:
void fcn(const int i) { }
void fcn(int i) { }
并不构成函数重载。因为上边的 const 被忽略掉了。
指针或引用形参与 const
int i = 0;
const int ci = i;
string::size_type ctr = 0;
reset(&i); // 调用的 reset (int *i) 形式
reset(&ci); // 不正确,不能用 const int 对象的指针初始化 int *
reset(i); // 调用的 reset(int &i) 形式
reset(ci); // 不正确,不能把普通引用绑定到 const 对象 ci 上
reset(42); // 不正确,不能把普通引用绑定到字面值上,(const 引用可以)
reset(ctr); // 错误,类型不对
尽量使用常量引用
把函数不会改变的形参定义成普通引用是一种比较常见的错误,这么做带给函数的调用者一种误导,即函数能够修改它的实参的值。此外,使用引用而非常量引用极大地限制函数能够接受的实参类型。如上,不能把 const 对象、字面值 或者需要类型转换的对象传给普通的引用形参。
有函数:
string::size_type find_char(const string &s,char c,string::size_type &occurs);
若定义成:
string_size_type find_char(string &s,char c,string::size_type &occurs);
那么在调用 find_char(“hello world”, ‘o’, ctr) 时,将发生编译错误、。、、
还有一个问题:
假如有正确的函数 is_sentence(const string &s),但是 string_size_type find_char(string &s,char c,string::size_type &occurs);
若函数 is_sentence 是这样的:
bool is_sentence(const string &s) {
string::size_type ctr = 0;
return find_char(s, '.', ctr) == s.size() - 1 && ctr == 1;
}
那么也会编译出错。
解决方法是: 将 is_sentence 也定义为 string &,但这样只是转移错误。 正确的: 在 is_sentence 中定义一个 string,作为 s 的副本,再传给 find_char,或者修改 find_char 为 const string &
那么 const string& 的好处:可以处理传入实参直接使用 字面值的情况,可以传入C风格字符串,减小局限性,同时可以避免长字符串的拷贝,提示效率。
测试代码:
#include <bits/stdc++.h>
using namespace std;
bool Is_empty(const string &s) {
return s.empty();
}
int main() {
char ch[] = "hhh";
cout << Is_empty("") << endl;
cout << ch << endl;
return 0;
}
对比代码:
#include <bits/stdc++.h>
using namespace std;
bool Is_empty(string &s) {
return s.empty();
}
int main() {
char ch[] = "hhh";
cout << Is_empty("") << endl; //!!!!报错
cout << Is_empty(ch) << endl; //!!!!报错
return 0;
}
数组形参 和 const
首先需要知道数组的两个特性: 数组不允许拷贝,使用数组时(通常)将其转化为指针。
所以呢,当函数传递一个数组时,实际上传的是指向数组首元素的指针。
尽管不能以传递值的方式传递数组,但是可以把形参写成类似数组的形式:
void print(const int *);
void print(const int[]);
void print(const int[10]); // 这里的维度表示我们期望数组含有多少个元素,实际上不一定.
以上三个函数方式是等价的,每个函数的形参都是 const int *,调用时只检查传入的参数是否是 const int *
int i = 0,j[2] = {0 , 1};
print(&i); // 正确, &i 是 int * 类型
print(j); // 正确,j 转换成 int * 并指向 j[10]
如果我们传给 print 函数的是一个数组,则实参自动转换成指向数组首元素的指针,数组的大小对函数的调用没有影响。
!!!注意以数组作为形参的函数也必须确保使用数组时不会越界。
由于数组是以指针的形参传递给函数的,所以并不知道数组的确切尺寸。对于这个,有三种常用的技术:
- 数组本身自带结束标记,如C风格字符串,自带 ‘\0’,
void print(const char *p) {
if(p) {
while(*p)
cout << *p ++;
}
}
这种适用于结束标记不会与普通数据混淆的情况。
- 使用标准库规范,利用数组首元素和尾后元素指针
void print(const int *beg,const int *end) {
while(beg != end) {
cout << *beg ++ << endl;
}
}
调用,如:
int j[2] = {0 , 1};
print(begin(j),end(j));
- 显示传递数组的大小
void print(const int *ia, size_t size) {
for(size_t i = 0;i < size;++ i) {
cout << ia[i] << endl;
}
}
///调用
int j[] = {0, 1};
print(j,end(j) - begin(j));
只要传递给函数的size值不超过实际大小,就是安全的。
数组形参和 const
三个 print 函数都把数组定义成了指向 const 的指针,关于引用的讨论同样适用指针。当函数不需要对数组元素进行写操作时,数组形参应该是指向 const 的指针。只有当函数确实要改变数组元素值时,才把形参定义成指向非常量的指针。
数组引用形参
c++允许将变量定义成数组的引用。同理,形参也可以是数组的引用,此时引用绑定到对应的实参上。
void print(int (&arr)[10]) {
for(auto it : arr) {
cout << it << endl;
}
}
但是和前面的有所不同,此函数只能用于大小为10的数组:
int i = 0,j[2] = {0,1};
int k[10] = {0,1,2,3,4,5,6,7,8,9};
print(&i); //错误
print(j); //错误
print(k); //正确
传递多维数组
多维数组其实是数组的数组,所以首元素是一个指向数组的指针。即
void print(int (*matrix)[10],int rowsize) { /* */ } ///括号是不能省略的,有括号和没括号意义不同
//等价定义
void print(int matrix[][10],int rowsize) { /* */ }
需要注意,数组第二维以及后面的更高维的大小都是不能省略的。
来源:CSDN
作者:Error Man
链接:https://blog.csdn.net/no_O_ac/article/details/104102648