1. 函数

函数是 C++ 程序中用于封装可执行逻辑的基本单元,将一段完成特定功能的代码组织为可复用的整体。定义函数的语法如下:

返回值类型 函数名(参数类型1 参数1, 参数类型2 参数2, ...)
{
    // 函数体,执行具体的逻辑
    return 返回值;
}

注: 对于返回值类型为void的函数,可以不写return语句,或直接写return;提前终止。


1.1 函数的声明与定义

函数的声明和定义是两个不同的概念,遵循单一定义规则(ODR):一个程序中函数只能有一次定义,但可以有多次声明。

  • 函数声明:也叫函数原型,仅告诉编译器函数的名称、返回类型和参数列表,不包含函数体。
  • 函数定义:包含完整的函数体,是函数的具体实现。
int add(int a, int b);       // 函数声明

int main()
{
    int res = add(10, 2);    // 在声明后即可调用函数
    return 0;
}

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

1.2 尾置返回类型

C++11引入,可以将返回类型写在参数列表后,用auto占位,->后接真实的返回类型,往往在一些返回类型很复杂的场景中会用到:

auto add(int a, int b) -> int
{
    return a + b;
}

auto get_func() -> int(*)(int, int)
{
    return add;
}

1.3 数组作为函数参数

数组在作为函数参数的时候,会退化为指向首元素的指针,丢失数组长度的信息,以下三种写法完全等价:

void func(int arr[]);
void func(int arr[10]);    // 这里的10没有实际约束意义
void func(int* arr);

如果要保留数组的长度,可以采取以下两种写法:

void func(int* arr, int n);    // 额外传入一个参数当数组的长度
void func(int (&arr)[10]);     // 直接传入数组的引用

多维数组作为参数的时候,第一维会退化为指针,而其余维度必须明确指定:

void func(int arr[][3]);
void func(int (*arr)[3]);

2. 默认参数

可以在函数声明中指定形参为某个默认值,调用时可以不传入对应实参,自动使用默认值。

#include <iostream>

int calc(int a, int b, int k = 3)
{
    return (a + b) * k;
}

int main()
{
    std::cout << calc(1, 2) << std::endl;
    std::cout << calc(1, 2, 3) << std::endl;
}

函数默认参数有以下要求:

1. 默认参数必须放在最右侧,不允许跳过某个参数为左边参数设置默认值

// 错误写法
void func(int a, int b = 3, int c, int d = 20);

2. 如果函数的声明与定义分开,只在声明中指定默认值

// 声明
void print(int a, int b = 10, int c = 20);

// 定义
void print(int a, int b, int c) 
{
    std::cout << a << " " << b << " " << c << std::endl;
}

// 调用
print(1);          // 输出 1 10 20
print(1, 2);       // 输出 1 2 20
print(1, 2, 3);    // 输出 1 2 3

3. 函数重载

函数重载是指在同一作用域内,用同一个函数名定义多个功能相似但参数不同的函数,由编译器在编译阶段根据调用的实参自动匹配对应版本。

  1. 作用域相同
  2. 函数名完全一致
  3. 参数列表不同:
    • 参数的个数不同
    • 参数的类型不同
    • 参数类型的排列顺序不同
int func(int x, int y);
double func(double x, double y);

3.1 注意事项

1. 返回值的类型并不参与函数的重载

下面例子不构成函数重载,而是同一个函数的重定义:

int func(int x);
int func(int y);

2. 顶层const不构成重载,底层const则构成

  • 顶层const:修饰变量本身的值不可变,对编译器而言是同一种参数:
  • 底层const:修饰指针/引用指向的内容,语义上有区别
// 错误操作:顶层 const 不构成重载,因而出现重复定义错误
void func(int a);
void func(const int a);

// 正确操作:底层 const 构成重载
void func(int* p);
void func(const int* p);

3. 默认参数可能会引发二义性,致使重载失败

void test();
void test(int x = 10);

test();         // 调用函数时可以同时匹配两个函数,编译器会进行报错

3.2 底层实现

TODO