你真的会声明C++的函数吗?

看到这个标题你可能会觉得很奇怪,函数声明谁不会啊?划走吧。

且慢,看了这篇文章,你也许会怀疑学了假的C++。

我们都知道,一个函数要在调用它之前的位置声明:

1
2
3
4
5
6
7
void func(); //函数声明
int main(){
func(); //调用函数
}
void fun(){ //函数定义
//do something
}

对于单文件程序来说,如果函数在调用它的位置前定义,可以不用声明:

1
2
3
4
5
6
void func(){ //函数定义
//do something
}
int main(){
func(); //调用函数
}

如果返回值类型相同,你可以在一行内声明多个函数:

1
double func1(double, double), func2(int);

甚至还能一边定义变量一边声明函数:

1
double var1, func(double);

不过要注意的是,声明的函数的返回值类型是否为指针或引用,并不是看最前面的基本数据类型是否有*或&,比如:

1
double *var1, func1(), *func2();

func1的返回值类型是double,而不是double*。func2的返回值类型才是double*

这不小菜一碟?接下来上主菜:


我们都知道,C++函数指针是这样写的:

1
2
3
int main(){
double (*func)(double, double);
}

变量func指向一类返回值类型为double,且有两个double类型的形参的函数。

那么,如果这样写会怎么样呢?

1
2
3
4
int main(){
double func1(double, double);
double *func2(double, double);
}

第一眼看上去好像还是函数指针,特别是func2,但仔细看,这不是函数的声明吗?咋就把它写到主函数内了呢?

其实是这样的,函数的声明既可以写到函数外,也可以写到函数内。如果在main函数内声明,则被声明的函数在被定义前只在main函数内起作用:

1
2
3
4
5
6
7
8
9
10
int main(){
double *func(double, double); //函数声明
double *value = func(0.5, 1.5); //函数调用
}
void test(){
func(1, 2); //错误,函数func未定义
}
double *func(double a, double b){ //函数定义
...
}



要不我们再把函数的声明放在更奇怪的位置上?

1
2
3
double invoke(double f(double, double), double a, double b){
return f(a, b);
}

好家伙,现在还把函数声明放在函数的形参上了。

当然这是合法的。为了调用这个函数,我们可以给他符合的函数做实参:

1
2
3
4
5
6
double add(double a, double b){
return a + b;
}
int main(){
double res = invoke(add, 0.5, 1.5);
}

我们常用typedef给函数指针类型起别名:

1
2
typedef double (*func)(double, double);
func f;

但如果:

1
typedef double func(double, double);

则是:定义一种类型func,可以用于声明 某种返回值类型为double,形参为两个double 的函数

同理,我们就可以把上面一系列代码写为:

1
2
3
4
5
6
7
8
9
10
11
12
13
typedef double func(double, double);

double invoke(func f, double a, double b){
return f(a, b);
}

double add(double a, double b){
return a + b;
}

int main(){
double res = invoke(add, 0.5, 1.5);
}

上面的代码还不足以说明func是 某种函数的声明 的类型,下面举另外一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
typedef double d_dd(double, double);

d_dd add; //声明函数add
d_dd multiply; //声明函数multiply

int main(){
std::cout << add(0.5, 1.5) << std::endl;
std::cout << multiply(0.5, 1.5) << std::endl;
}

double add(double a, double b){ //定义函数add
return a + b;
}

double multiply(double a, double b){ //定义函数multiply
return a * b;
}

可以看到,d_dd add;指的是double add(double, double);d_dd multiply同理,使用d_dd可以用来声明多个同返回值、同类型形参的函数。


在C++11之后,可以使用using来替代typedef,同理:

1
2
3
using d_dd = double(double, double);

d_dd add, multiply; //声明两个函数

这里回答一下封面图各行代码的意义:

  • double func;:定义一个double类型的变量func
  • double func();:声明一个返回值类型为double,无形参的函数func
  • double *func();:声明一个返回值类型为double*,无形参的函数func
  • double (*func)();:定义一个函数指针func,可以指向一类返回值类型为double,无形参的函数
  • typedef double func;:定义一种类型func,原型为double。
  • typedef double func();:定义一种类型func,可以用来声明 某个返回值类型为double,无形参 的函数。
  • typedef double (*func)();:定义一种类型func,原型为 返回值类型为double,无形参 的函数指针。
  • using func = double;:定义一种类型func,原型为double
  • using func = double();定义一种类型func,可以用来声明 某个返回值类型为double,无形参 的函数。
  • using func = double(*)();定义一种类型func,原型为 返回值类型为double,无形参 的函数指针。

提问:尝试解释以下代码含义:

  1. double*(*func)();
  2. double* *func();
  3. double (func)();

答案:

  1. 定义一个函数指针func,可以指向一类返回值类型为double*,无形参的函数
  2. 声明一个返回值double**,无形参的函数func,或许写成double** func();会更好看一些
  3. double func();

当函数指针和数组搭配时,你可能会看到诸如int *(*(*f)[5])();的代码,为了理解它,你可以阅读 当函数指针与数组相遇


你真的会声明C++的函数吗?
https://blog.lyzen.cn/2022/08/18/CppFunctionDeclaration/
作者
Lyzen
发布于
2022年8月18日
许可协议