C++ primer 学习笔记之函数

这里是学习C++ primer做的笔记

返回数组指针

数组不能被拷贝,所以函数不能返回数组,不过,函数可以返回数组的指针或引用。

1
2
3
typedef int arrT[10];//arrT是一个类型别名,它表示的类型是含有10个整数的数组。(这里有点不好理解)
using arrT=int[10];//和上面的等价
arrT* func(int i);//func返回一个指向含有10个整数的数组指针

声明一个返回数组指针的函数,如下:

1
int (*func(int i))[10];

上面的含义是,返回一个指向大小为10的数组指针,数组中的元素是整形。C++ 11新标准使用尾置返回类型,在本应该出现返回类型的地方放置一个auto

1
2
//func接受一个int类型的实参,返回一个指针,该指针指向含有10个整数的数组
auto func(int i) -> int(*)[10];

也可以使用decltype,如下,我们只知道返回一个指针的类型,但是不知道到底返回指向那个数组的指针:

1
2
3
4
5
int odd[]={1,3,5,7,9};
int even[]={0,2,4,6,8};
decltype(odd) *arrPtr(int i){
return (i%2)? &odd:&even;//返回一个指向数组的指针
}

decltype并不负责把数组类型转换成对应的指针,所以decltype的结果是个数组,要想表示arrPtr返回指针还必须在函数声明时加一个*符号。

函数重载

如果同一作用域内的几个函数名字相同但是形参列表不同,我们称之为重载函数。
不允许两个函数除了返回类型外其他所有要素都相同。如下:

1
2
Record lookup(const Account&);//可以省略形参的名字
bool lookup(const Account&);//错误:与上一个函数相比只有返回类型不同

重载与const形参

这个之前有提到过,一个拥有顶层const的形参无法和另一个没有顶层const的形参区分开来:

1
2
3
4
5
Record lookup(Phone);
Record lookup(const Phone);//重复声明了Record lookup(Phone)
Record lookup(Phone*);
Record lookup(Phone* const);//重复声明了Record lookup(Phone*)

但是底层const就可以实现重载:

1
2
3
4
5
Record lookup(Account&);//函数作用于Account的引用
Record loopup(const Account&);//新函数,作用于常量引用
Record lookup(Account*);//新函数,作用于指向Account的指针
Record lookup(const Account*);//新函数,作用于指向常量的指针

内联函数和constexpr函数

在大多数机器上,一次函数调用其实包含着一系列工作:调用前要先保存寄存器,并在返回时恢复;可能需要拷贝实参;程序转向一个新的位置继续执行。
将函数指定为内联函数,通常就是将它在每个调用点上“内联地”展开,比如下面的函数定义为内联函数:

1
2
3
inline const string &shorterString(const string &s1,const string &s2){
return s1.size()<=s2.size()?s1:s2;
}

这样我们调用shorterString函数的时候就会如下展开:

1
2
cout<<shorterString(s1,s2)<<endl;
cout<<(s1.size()<s2.size()?s1:s2)<<endl;

constexpr函数是指用于常量表达式的函数,其函数的返回类型及所有形参的类型都得是字面值类型,而且函数体中必须有且只有一条return语句:

1
2
constexpr int new_sz(){return 42;}
constexpr int foo=new_sz();

允许constexpr函数返回值并非是一个常量,也可以是常量表达式:

1
2
//如果arg是常量表达式,则scale(arg)也是常量表达式
constexpr size_t scale(size_t cnt){return new_sz()*cnt};

函数指针

函数指针指向的是函数而非对象,函数的类型由它的返回类型和形参类型共同决定,与函数名无关。
比如下面的函数:

1
bool lengthCompare(const string &, const string &);

该函数的类型是bool(const string&, const string&),要想申明一个可以指向该函数的指针,只需要用指针替换函数名即可:

1
2
//pf指向一个函数,该函数的参数是两个const string的引用,返回值是bool类型
bool(*pf)(const string &,const string &);

这里*pf两端的括号必不可少,如果不写这对括号,则pf是一个返回值为bool指针的函数:

1
2
//声明一个名为pf的函数,该函数返回bool*
bool *pf(const string &, const string &);

使用函数指针:

1
2
3
4
5
6
7
pf=lengthCompare;//pf指向名为lengthCompare的函数
pf=&lengthCompare;//等价的赋值语句,其中的取地址符是可以选的
//直接使用指向函数的指针调用该函数,无需提前解引用指针
bool b1=pf("hello","goodbye");//调用lengthCompare函数
bool b2=(*pf)("hello","goodbye");//一个等价的调用,可以不用使用解引用
bool b3=lengthCompare("hello","goodbye");//另一个等价的调用

重载函数的指针:

1
2
3
void ff(int*);
void ff(unsigned int);
void (*pf1)(unsigned int)=ff;//pf1指向ff(unsigned)

函数指针形参:
虽然不能定义函数类型的形参,但是形参可以是指向函数的指针。

1
2
3
4
//第三个形参是函数类型,它会自动地转换成指向函数的指针
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 &));

可以直接把函数作为实参使用:

1
useBigger(s1,s2,lengthCompare);

直接使用函数指针显得冗长,类型别名和decltype能让我们简化使用函数指针的代码:

1
2
3
4
5
6
7
//Func和Func2是函数类型
typedef bool Func(const string&, const string&);
typedef decltype(lengthCompare) Func2;//与上面的等价
//FuncP和FuncP2是指向函数的指针
typedef bool(*FuncP)(const string&, const string&);
typedef decltype(lengthCompare) *FuncP2;//等价的类型

注意decltype的结果是函数类型,所以只有在结果前面加上*才能得到指针。

返回指向函数的指针:

1
2
3
4
5
6
using F=int(int*, int);//F是函数类型,不是指针
using PF=int(*)(int*, int);//PF是指针类型
PF f1(int);//正确:PF是指向函数的指针,f1返回指向函数的指针
F f1(int);//错误:F是函数类型,f1不能返回一个函数
F *f1(int);//正确:显示指定返回类型是指向函数的指针

直接声明:

1
int (*f1(int))(int*, int);

如果觉得有帮助,给我打赏吧!