目录
C++Primer 函数
1、参数传递
1.1、含有可变形参的函数
1.2、数组形参
2、返回类型和return语句
2.1、列表初始化返回值
2.2、返回数组指针
3、函数重载
3.1、顶层const与底层const
3.2、const_cast与static_cast
3.3、const_cast与重载
4、函数指针
4.1、使用函数指针
4.2、重载函数的指针
4.3、函数指针形参
4.4、返回指向函数的指针
写在前面,函数中很基础的概念参数传递,如果形参是引用类型,它将绑定到对应的实参上;否则,将实参的值拷贝后赋给形参。
需要注意的是const:下面的这个函数虽然合法,但不算特别有用。
bool is_empty(string &s)
{
return s.empty();
}
局限主要在于两个方面:
1.容易给使用者一种误导,即程序允许修改变量s的内容;
2.限制了该函数所能接受的实参类型,我们无法把const对象、字面值常量或者需要进行类型转换的对象传递给普通的引用形参。
综上,应该修改为:
bool is_empty(const string &s)
{
return s.empty();
}
如果函数的实参数量未知但是全部实参的类型都相同,可以使用initializer_list类型的形参。initializer_list是一种标准库类型,用于表示特定类型的值的数组,initializer_list定义在同名头文件中。
下面用代码举例说明:
函数的参数是initializer_list
int iCount(initializer_list il)
{
int count = 0;
for(auto val:il)
count+= val;
return count;
}
int main()
{
cout<< "1+4+5 = " << iCount({1,4,5}) <
数组的两个特殊性质对我们定义和使用作用在数组上的函数有影响,这两个性质分别是:
因为不能拷贝数组,所以我们无法用值传递的方式使用数组参数。因为数组会被转换成指针,所以当我们为函数传递一个数组时,实际上传递的是指向数组首元素的指针。
void print(const int *)
void print(const int[])
void print(const int[10])
上述三行代码,尽管形式不同,但这三个print函数是等价的,即使有维度,表示的是我们期望数组含有多少元素,实际不一定。每个函数的唯一形参都是const int *类型的。
C++允许将变量定义成数组的引用,基于同样的道理,形参也可以是数组的引用。此时,引用形参绑定到对应的实参上,也就是绑定到数组上。数组引用形参:
void print(int (&arr)[10]) //维度是类型的一部分
{
for(auto elem: arr)
cout<
注:&arr两端的括号不可少
f(int &arr[10]) //错误:将arr声明成了引用的数组 f(int (&arr)[10]) //正确:arr是具有10个整数的整型数组的引用
函数可以返回花括号包围的值的列表。类似于其他返回结果,此处的列表也用来对表示函数返回的临时量进行初始化。如果列表为空,临时量执行值初始化;否则返回的值由函数的返回类型决定。
如果函数返回的是内置类型,则花括号包围的列表最多包含一个值,如果函数返回的是类类型,由类本身定义初始值如何使用。这里还要提到一个{},与()的区别:
因为数组不能被拷贝,所以函数不能返回数组。不过,函数,可以返回数组的指针或引用。由下面例子进行说明:
编写一个函数的声明,使其返回数组的引用并且该数组包含10个string对象。同时再为这个函数写三个声明,一个使用类型别名,另一个使用尾置返回类型,最后一个使用decltype关键字。
string (&fun())[10];
以上面声明为例含义为:fun()表示调用fun函数无需任何实参,(&fun())表示函数的返回结果是一个引用,(&fun())[10]表示引用的对象是一个维度为10的数组,string(&fun())[10]表示数组的元素是string对象。
//类型别名
typedef string arr[10];
arr &fun();
using arr = string[10];
arr &fun();
//尾置返回类型
auto fun()->string (&) [10];
//decltype关键字
string str[10];
decltype(str) & fun();
string words[10] = {"word","count","end"};
string (&fun())[10]
{
return words;
}
int main()
{
string result[10] = fun();
for(auto i : result)
{
if( i == "")
continue;
cout<
在用指针举例:表示接受一个整型参数的函数,并可以对函数调用的结果执行解引用操作。解引用的结果将得到一个大小是5的数组,数组中的元素是int类型。
int (*func(int i))[5];
//odd是数组名,可以是指针,指向数组首元素
int odd[] = {1,3,5,7,9};
int even[] = {2,4,6,8,10};
//函数返回的是一个指针,指向五个整数的数组
int (*func(int i))[5]
{
return ((i%2)?&odd:&even);
}
//等价于decltype(odd) *func(int i);
int main()
{
int (*number)[5];
number = func(2);
for(int i =0;i<5;i++)
{
cout<<(*number)[i]<<" ";
}
cout<
需要注意的是decltype,他并不负责吧数组类型转换成对应的指针,所以decltype(odd)的结果是个数组,所以要表示func返回指针还必须在函数声明时加个*。
decltype(odd) *func(int i);
如果同一作用域内的几个函数名字相同但形参列表不同,我们称之为重载函数。
指针本身是个常量,可以表示任意的对象是常量。
表示指针所指的对象是一个常量。底层const与指针和引用等符合类型的基础类型部分有关。
在函数重载中,一个拥有顶层const的形参无法和另一个没有顶层const的形参区分开来。
Record lookup(Phone);
Record lookup(const Phone); //顶层const,重复声明
Record lookup(Phone *);
Record lookup(Phone * const); //顶层const,重复声明
Record lookup(Account &);
Record lookup(const Account &);//底层const,新函数
Record lookup(Account *);
Record lookup(const Account *);//底层const,新函数
const_cast只能改变运算对象的底层const
static_cast任何具有明确定义的类型转换,只要不包含底层const,都可以使用static_cast。
利用代码举例说明:
const string &shorterString(const string &s1,const string &s2)
{
return s1.size()<= s2.size()?s1:s2;
}
string &shorterString(string &s1,string &s2)
{
auto &r = shorterString(const_cast(s1),const_cast(s2));
return const_cast(r);
}
函数指针指向的是函数而非对象。和其他指针一样,函数指针指向某种特定类型。函数的类型由它的返回类型和参数类型共同决定,与函数名无关。在函数指针知识点里会涉及到函数指针形参,就是函数指针作为函数的一个参数,要注意的是无论在当做参数的函数名前有没有*,都会被当做是函数指针。还会涉及到返回指向函数的指针。需要之前就定义好返回类型相同的函数。
bool lengthCompare(const string&,const string&);
该函数类型是bool(const string&,const string&).要想声明一个可以指向该函数的指针,只需要用指针替换函数名即可:
bool (*pf)(const string&,const string&);//未初始化,无法定义
从我们声明的名字开始观察,pf前面有个*,因此pf是指针;右侧是形参列表,表示pf指向的是函数;在观察左侧,发现函数的返回类型是布尔值。因此,pf就是一个指向函数的指针,其中该函数的参数是两个const string的引用,返回值是bool类型。
当我们把函数名作为一个值使用时,该函数就自动的转换成指针。
pf = lengthCompare; //pf指向名为lengthCompare的函数
pf = &lengthCompare; //等价的赋值语句:取地址符是可选的
此外,我们还能直接使用指向函数的指针调用该函数,无需提前解引用指针:
pf = lengthCompare;
bool b1 = pf("Hello","goodbye");
bool b2 = (*pf)("Hello","goodbye");
bool b3 = lengthCompare("Hello","goodbye");
在指向不同函数类型的指针间不存在转换规则。但是和往常一样,我们可以为函数指针赋一个nullptr或者值为0的整型常量表达式,表示该指针没有指向任何一个函数:
string::size_type sumLength(const string&,const string&);
bool cstringCompare(const char*,const char*);
pf = 0; //正确:pf不指向任何函数
pf = sumLength; //错误:返回类型不匹配
pf = cstringCompare; //错误:形参类型不匹配
pf = lengthCompare; //正确
举例说明:
void ff(int*);
void ff(unsigned int);
void (*pf1)(unsigned int) = ff;//pf1指向ff(unsigned)
编译器通过指针类型决定选用哪个函数,指针类型必须与重载函数中的某个精确匹配
void (*pf2)(int) = ff; //错误:没有任何一个ff与该形参列表匹配
double (*pf3)(int*) = ff; //错误:ff和pf3的返回类型不匹配
和数组类似,虽然不能定义函数类型的形参,但是形参可以是指向函数的指针。此时,形参看起来是函数类型,实际上却是当成指针使用。
//第三个形参是函数类型,会自动转换成指向函数的指针
void useBigger(const string &s1,const string &s2,bool pf(const string&,const string &));
void useBigger(const string &s1,const string &s2,bool (*pf)(const string&,const string &));
我们可以直接把函数作为实参使用,此时它会自动转换成指针:
useBigger(s1,s2,lengthCompare);
为简化使用函数指针代码:
//Func和Func2是函数类型
typedef bool Func(const string&,const string&);
typedef decltype(lengthCompare) Func2;
//FuncP和FunP2是指向函数的指针
typedef bool (*FuncP)(const string&,const string&);
typedef decltype(lengthCompare) (*FuncP2);
void useBigger(const string&,const string&,Func);
void useBigger(const string&,const string&,Func2);
和数组类似,虽然不能返回一个函数,但是能返回指向函数类型的指针。然而,我们必须把返回类型写成指针类型,编译器不会自动的将函数返回类型当成对应的指针类型处理。三种方法声明一个返回函数指针的函数:
1.类型别名:
using F = int(int*,int); //F是函数类型,不是指针
using PF = int(*)(int*,int); //PF是指针类型
typedef int (*p)(int*,int); //p是指向函数的指针
PF f1(int); //f1返回指向函数的指针
p f1(int);
2.直接声明:
int (*f2(int))(int*,int);
3.尾置返回类型:
auto f3(int)->int(*)(int*,int);
string::size_type sumLength(const string &str1, const string &str2)
{
cout << "调用了sumLength" << endl;
return str1.size() + str2.size();
}
string::size_type largerLength(const string &str1, const string &str2)
{
cout << "调用了largerLength" << endl;
return (str1.size() >= str2.size()) ? str1.size() : str2.size();
}
decltype(sumLength) *getFcn(const string &s)
{
if (s == "larger")
return largerLength;
else
return sumLength;
}
int main()
{
cout << getFcn("larger")("largerlength", "sumlength") << endl;
//等同于
//cout<<(*getFcn("larger"))("largerlength","sumlength")<
如上,如果输入的参数为larger,则根据判断条件会调用largerLength函数。返回如下图: