函数部分知识大致已经掌握,此为复习,因此记录学习过程从简。
1.函数基础
一个典型的函数定义包括以下几个部分:返回类型、函数名称、由0个或者多个形参组成的列表以及函数体。
1.1 局部对象
**自动对象**
只存在于块执行期间的对象称之为自动对象。形参为一种自动对象,函数开始时为形参申请存储空间,而
函数终止,则形参被销毁。
**局部静态对象**
令局部变量的生命周期贯穿函数调用及之后的时间,可将局部变量定义为static类型,该对象在程序终止时
才会被销毁。
1.2 函数声明
**在头文件中进行函数声明**
1.3 分离式编译
**编译和链接多个源文件**
2.参数传递
每次调用函数时都会重新创建它的形参,并用传入的实参对形参进行初始化。初始化形参的机理与初始化变量一样。
当实参的值被拷贝给形参时,形参和实参是两个相互独立的对象。
2.1 传值参数
当初始化一个非引用类型的变量时,初始值被拷贝给变量,此时,对变量的改动不会影响初始值。
**指针形参**
指针的行为和其他非引用类型一样,当执行指针拷贝操作时,拷贝的是指针的值。在C++语言中,建议使用引用类型
的形参替代指针。
2.2 传引用参数
**使用引用避免拷贝** : 如果函数无须改变引用形参的值,最好将其声明为常量引用。
例:
bool isShorter(const string &s1, const string &s2)
{
return s1.size() < s2.size();
}
**使用引用形参返回额外信息** : 若函数需要多个返回值,引用形参可以做为函数的返回值。
例:
string::size_type find_char (const string &s, char c,string::size_type &occurs)
{
auto ret = s.size();
occurs = 0;
for (decltype(ret) i =0;i!=s.size();++i)
{
if (s[i] == c)
{
if (s[i] == s.size())
ret =i; //记录c第一次出现的位置
++occurs; //将出现的次数加1
}
}
return ret; //出现次数通过occurs隐式地返回
}
2.3 const形参和实参
**顶层const**: 顶层const表示指针本身是一个常量,底层const表示指针指向的对象是一个常量。
int i =0;
int *const p1 = &i; //不能改变p1的值,这是一个顶层const
const int ci =42 ; //不能改变ci的值,这是一个顶层const
cosnt int *p2 = &ci; //允许改变p2的值,这是一个底层const
const int *const p3 = p2; //靠右的const是顶层const,靠左的是底层const
const int &r = ci; //用于声明引用的const都是底层const
2.4 数组形参
数组的性质是:1.不允许拷贝数组;2.使用数组时(通常)会将其转换成指针。
以下三个函数等价:
void print (const int*);
void print (const int[]);
void print (const int[10]);
如果我们传给print函数的是一个数组,则实参自动地转换成指向数组首元素的指针,数组的
大小对函数的调用没有影响。
**管理数组实参**
第一种方法是要求数组本身包含一个结束标记:
void print (const char *cp)
{
if (cp) //若cp不是一个空指针
while (*cp) //只要指针所指的字符不是空字符
cout << *cp++; //输出当前字符并指针向前移动一个位置
}
第二种方法是传递指向数组首元素和尾后元素的指针:
void print (const int *beg,const int *end)
{
while (beg != end)
cout << *beg++ <<endl; //输出当前元素并将指针向前移动一个位置
}
第三种方法是专门定义一个表示数组大小的形参
void print (const int ia[],size_t size)
{
for (size_t i=0;i !=size; i++)
{
cout <<ia[i]<<endl;
}
}
当函数不需要对数组元素执行写操作的时候,数组形参应该是指向const的指针。
void print (int (&arr)[10]) //&arr两端的括号必不可少,arr是具有10个整数的整型数组的引用
{
for (auto elem : arr)
cout << elem << endl;
}
当将多维数组传递给函数时,真正传递的是指向数组首元素的指针。因为我们处理的是多维数组(数组的数组),
所以首元素本身是一个数组,指针就是指向数组的指针。数组的第二维(以及后面的所有维度)的大小都是数组
类型的一部分,不能省略:
//matrix指向数组的首元素,该数组的元素是由10个整数构成的数组
void print (int (*matrix)[10],int rowsize) {}
等价于:
void print (int matrix[][10],int rowsize) {}
2.5 含有可变形参的函数
如果函数的实参数量未知但是全部实参的类型都相同,我们可以使用initializer_list类型的实参。
initializer_list是一种模板类型,定义其对象时,必须说明列表中所含元素的类型:
initializer_list<string> ls ; initializer_list <int> li;
我们使用如下的形式编写输出错误信息的函数,使其可以作用于可变数量的实参:
void error_msg (initializer_list <string> il)
{
for (auto beg = il.begin();beg != il.end();++beg)
{
cout << *beg <<"";
cout << endl;
}
}
如果想向initializer_list形参中传递一个值的序列,则必须把序列放在一对花括号内:
// expcted 和 actual 是string 对象
if (expected != actual)
{
error_msg ({"functionX",expected,actual});
error_msg ({"functionX","okay"});
}
含有initializer_list形参的函数也可以同时拥有其他形参。
void error_msg (ErrCode e,initializer_list <string> il)
{
cout << e.msg() <<":";
for (const auto &elem : il)
cout << elem << " ";
cout << endl;
}
调用上面的函数,其表达式为
if (expected != actual)
error_msg(ErrorCode(42),{"functionX",expected,actual});
else
error_msg(ErrCode(0),{"functionX","okay"});
3.返回类型和return语句
无返回值函数、有返回值函数、递归
3.3 返回数组指针
int arr[10]; //arr是一个含有10个整数的数组
int *p1[10]; //p1是一个含有10个指针的数组
int (*p2)[10]; //p2是一个指针,它含有10个整数的数组
返回数组指针的函数形式如下图所示:
Type (*function(parameter_list)) [dimension]
例: int (*function(int i))[10];
声明的含义:func(int i) 表示调用func函数需要一个int类型的实参
(*func(int i)) 表示我们可以对函数调用的结果执行解引用操作
(*func(int i)) [10] 表示解引用func的调用将得到一个大小是10的数组
int (*func(int i)) [10] 表示数组中的元素是int类型
**使用decltype**
int odd[] = {1,3,5,7,9};
int even[] = {0,2,4,6};
//返回一个指针,该指针指向含有5个整数的数组
decltype(odd) *arrPtr(int i)
{
return ( 1 % 2) ? &odd : &even; //返回一个指向数组的指针
}
4.函数重载
5.内联函数和constexpr函数
6.函数指针